/*
* @(#)DocumentValidateManager.java
*
* Copyright (c) 2003 DCIVision Ltd
* All rights reserved.
*
* This software is the confidential and proprietary information of DCIVision
* Ltd ("Confidential Information").  You shall not disclose such Confidential
* Information and shall use it only in accordance with the terms of the license
* agreement you entered into with DCIVision Ltd.
*/
package com.dcivision.dms.core;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.sql.Connection;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.dcivision.dms.DmsErrorConstant;
import com.dcivision.dms.DmsOperationConstant;
import com.dcivision.dms.bean.DmsContent;
import com.dcivision.dms.bean.DmsDocument;
import com.dcivision.dms.bean.DmsDocumentDetail;
import com.dcivision.dms.bean.DmsRoot;
import com.dcivision.dms.bean.DmsValidation;
import com.dcivision.dms.bean.DmsVersion;
import com.dcivision.dms.bean.MtmDocumentRelationship;
import com.dcivision.dms.dao.DmsContentDAObject;
import com.dcivision.dms.dao.DmsDocumentDAObject;
import com.dcivision.dms.dao.DmsRootDAObject;
import com.dcivision.dms.dao.DmsVersionDAObject;
import com.dcivision.dms.dao.MtmDocumentRelationshipDAObject;
import com.dcivision.dms.web.ListDmsDocumentForm;
import com.dcivision.framework.ApplicationContainer;
import com.dcivision.framework.ApplicationException;
import com.dcivision.framework.ErrorConstant;
import com.dcivision.framework.GlobalConstant;
import com.dcivision.framework.MessageResourcesFactory;
import com.dcivision.framework.PermissionManager;
import com.dcivision.framework.SessionContainer;
import com.dcivision.framework.SystemParameterConstant;
import com.dcivision.framework.SystemParameterFactory;
import com.dcivision.framework.TextUtility;
import com.dcivision.framework.Utility;
import com.dcivision.framework.bean.SysUserDefinedIndex;
import com.dcivision.framework.bean.SysUserDefinedIndexDetail;
import com.dcivision.framework.dao.SysUserDefinedIndexDAObject;
import com.dcivision.upload.core.MultipleFileUploadUtil;
import com.dcivision.workflow.bean.MtmWorkflowProgressSystemObject;
import com.dcivision.workflow.bean.WorkflowProgressAttachment;
import com.dcivision.workflow.dao.MtmWorkflowProgressSystemObjectDAObject;
import com.dcivision.workflow.dao.MtmWorkflowRecordObjectLinkDAObject;
import com.dcivision.workflow.dao.WorkflowProgressAttachmentDAObject;
import com.dcivision.workflow.dao.WorkflowProgressDAObject;


/**
 * <p>Class Name:       DocumentValidateManager.java    </p>
 * <p>Description:      The class mainly handle the dmsDocument validation functions.</p>
 * @author              Zoe Shum
 * @company             DCIVision Limited
 * @creation date       01/08/2003
 * @version             $Revision: 1.73.2.25 $
 */

public class DocumentValidateManager {

  public static final String REVISION = "$Revision: 1.73.2.25 $";

  protected Log log = LogFactory.getLog(this.getClass().getName());
  private SessionContainer sessionContainer;
  private Connection conn;
  /**
   *  Constructor - Creates a new instance of DocumentValidateManager
   */
  public DocumentValidateManager(SessionContainer sessionContainer, Connection conn) {
    this.sessionContainer = sessionContainer;
    this.conn = conn;
  }

  /**
   *  Release - release all variable in instance of DocumentRetrievalManager
   */
  public void release () {
    this.sessionContainer = null;
    this.conn = null;
  }
  
  /**
   * Extends checkDuplicateNameSupport method to check duplicate name.
   * This method will add an cache object to application cache before insert document
   * It is important to clear the object from application cache after finished insert document.
   * lee.lv 06/11/29 EIP-539
   * @param dmsDocument
   * @return
   * @throws DuplicateNameException
   * @throws ApplicationException
   */
  public boolean checkAndLockDuplicateNameCache(DmsDocument dmsDocument) throws DuplicateNameException, ApplicationException {
    Boolean supportDuplicateName = new Boolean(SystemParameterFactory.getSystemParameter(SystemParameterConstant.DMS_SUPPORT_DUPLICATE_NAME));
    ApplicationContainer applicationContainer = sessionContainer.getAppContainer();
    if ( supportDuplicateName.booleanValue()==false && applicationContainer!=null) {
      boolean isSuccess = false;
      String oldDocumentName="";
      if(dmsDocument.getDocumentName().lastIndexOf(".")>-1){
        oldDocumentName = dmsDocument.getDocumentName().substring(0,dmsDocument.getDocumentName().lastIndexOf("."));
      }else{
        oldDocumentName = dmsDocument.getDocumentName();
      }
      int counter = 0;
      while (true) {
        //validate duplicated name from application cache
        isSuccess = applicationContainer.checkAndLockDuplicateNameCache(dmsDocument.getParentID(), dmsDocument.getDocumentName(), dmsDocument.getDocumentType());
        if (isSuccess) {
          //validate duplicated name from database
          isSuccess = checkDuplicateNameSupport(dmsDocument);
        }
        
        if (DmsDocument.DOCUMENT_LINK.equals(dmsDocument.getDocumentType())){
          if (oldDocumentName.length() > DmsOperationConstant.FILE_NAME_LENGTH_LIMIT) {
            throw new ApplicationException("errors.dms.shortcut_name_too_longer");
          }
        }
        
        if (DmsOperationConstant.DMS_CREATE_BY_UPLOAD.equals( dmsDocument.getCreateType()) && isSuccess==false) {
          if (DmsDocument.FOLDER_TYPE.equals(dmsDocument.getDocumentType())) {
            break;
          }else {
            // if document is created by upload tracker and check duplicate name failure, rename the document name and check again.
            String oldFileName = dmsDocument.getDocumentName();
            String fileNameOri = oldFileName.substring(0,oldFileName.lastIndexOf("."));
            String fileNameExt = oldFileName.substring(oldFileName.lastIndexOf("."));
            dmsDocument.setDocumentName(fileNameOri + "("+ (++counter)+ ")" + fileNameExt);
          }
        }else if(DmsDocument.DOCUMENT_LINK.equals(dmsDocument.getDocumentType()) && isSuccess==false){
          String oldFileName = dmsDocument.getDocumentName();
          String fileNameOri="";
          String fileNameExt="";
          if(oldFileName.indexOf(".")>-1){
            fileNameOri = oldFileName.substring(oldDocumentName.length(),oldFileName.lastIndexOf("."));
            fileNameExt = oldFileName.substring(oldFileName.lastIndexOf("."));
          }else{
            fileNameOri = oldFileName.substring(oldDocumentName.length());
          }
          if(fileNameOri.indexOf("(")>-1 && fileNameOri.indexOf(")")>-1){
            fileNameOri = "("+ (++counter)+ ")";
          }else{
            fileNameOri = "(1)";
            counter++;
          }
          if ( (oldDocumentName+fileNameOri+fileNameExt).length() > DmsOperationConstant.FILE_NAME_LENGTH_LIMIT) {
            throw new ApplicationException("errors.dms.shortcut_name_too_longer");
          }
          dmsDocument.setDocumentName(oldDocumentName+fileNameOri+fileNameExt);
        }else {
          if (isSuccess) {
            break;
          } else {
            throw new DuplicateNameException(DmsErrorConstant.DUPLICATE_FILE_NAME);
          }
        }
      }
      return isSuccess;
    }else {
      return true;
    }
  }

  /**
   * Checks duplicate document / folder name support in the same folder level.
   * If support, return true else false
   * If not support, return false if no duplicate name, throws DuplicateNameException
   * when duplicate name under same folder level
   * 
   * @param dmsDocument the dmsDocument object to check duplicate name
   * @return true if support duplicate name otherwise false
   * @throws DuplicateNameException
   */
  public boolean checkDuplicateNameSupport(DmsDocument dmsDocument) throws DuplicateNameException, ApplicationException {
    boolean checkDuplicateName = true;  //used to cater the uploaded document
    Boolean supportDuplicateName = new Boolean(SystemParameterFactory.getSystemParameter(SystemParameterConstant.DMS_SUPPORT_DUPLICATE_NAME));

    // check for invalid characters on document name only
    TextUtility.stringValidation(dmsDocument.getDocumentName());

    if ( supportDuplicateName.booleanValue() == false) {
      /*For uploaded folder with duplicate name existed; allow pass and will no need to create folder */
      if (DmsDocument.FOLDER_TYPE.equals(dmsDocument.getDocumentType()) &&
          DmsOperationConstant.DMS_CREATE_BY_UPLOAD.equals( dmsDocument.getCreateType())){
        if (checkNameExistence(dmsDocument)) {
          checkDuplicateName = false;
        }
      }else if(DmsDocument.DOCUMENT_LINK.equals(dmsDocument.getDocumentType())){
        if (checkNameExistence(dmsDocument)) {
          checkDuplicateName = false;
        }
      }
      else{
        if (checkNameExistence(dmsDocument)) {
          throw new DuplicateNameException(DmsErrorConstant.DUPLICATE_FILE_NAME);
        }
      }
    }
    return checkDuplicateName;
  }

  /**
   * Checks any the document / folder name already exists under the same folder 
   * <P>
   * Shortcut document is allowed to be same name with other document type document but
   * shortcut itself cannot be duplicate.
   * <P>
   * @param dmsDocument the dmsDocument object to check duplicate name
   * @return true if there is exists document in the same folder that have the same name
   * @throws ApplicationException
   */
  public boolean checkNameExistence(DmsDocument dmsDocument) throws ApplicationException {
    log.debug("Check Name Existence for document name = " + dmsDocument.getDocumentName());
    boolean existence = false;
    DmsDocumentDAObject documentDAO = new DmsDocumentDAObject(sessionContainer, conn);
  
    List existDocList = documentDAO.getDuplicateNameInSameLevel(dmsDocument);
    if (existDocList != null && !existDocList.isEmpty()) {
      for (int i=0; i<existDocList.size(); i++) {
        DmsDocument existDoc = (DmsDocument)existDocList.get(i);
        if (DmsDocument.DOCUMENT_LINK.equals(existDoc.getDocumentType()) && DmsDocument.DOCUMENT_LINK.equals(dmsDocument.getDocumentType())) {
          existence = true;
        } else if (!DmsDocument.DOCUMENT_LINK.equals(existDoc.getDocumentType()) && !DmsDocument.DOCUMENT_LINK.equals(dmsDocument.getDocumentType())) {
          existence = true;
        }
      }
    }
    documentDAO=null;
    return existence;
  }
  
  /**
   * Checks the deleted (marked deleted) parent document i.e. folder, compound document and 
   * paper document has the duplicate name with existing document under the same folder level.
   * <P>
   * It is used for the recycleBin restore function that restore of the document will check its
   * parent is deleted or not. If parent document is also deleted, the parent document will also 
   * restored. It is needed to check any existing document with same name with this restore 
   * parent document. 
   * <P>
   * This method check recursively to its ancestor document deleted that has duplicate name document
   * or not. 
   *
   * 
   * @param dmsDocument the dmsDocument object to check delete ancestor document has duplicate name
   *                    or not
   * @return true if there is duplicate name document with the ancestor document.
   * @throws ApplicationException
   */
  public boolean checkAncestorNameExistence(DmsDocument dmsDocument) throws ApplicationException {
    DmsDocumentDAObject dmsDocumentDAO = new DmsDocumentDAObject(sessionContainer, conn);
    boolean existence=false;
    DmsDocument parentDocument = null;
    parentDocument = (DmsDocument)dmsDocumentDAO.getInactiveByID(dmsDocument.getParentID());
    if(parentDocument!=null){
      if(this.checkNameExistence(parentDocument)){
       existence= true; 
      }else{
        existence = this.checkAncestorNameExistence(parentDocument);
      }
    }else{
      existence = false;
    }
    return existence;
   
  }

  /**
   * Checks the search criteria exists or not in the search object list.
   * 
   * @param searchObjectList List contains the search criteria
   * @throws DuplicateNameException  
   * @throws ApplicationException
   */
  public void checkSearchCriteriaExistence(List searchObjectList) throws DuplicateNameException, ApplicationException {
    if ( Utility.isEmpty(searchObjectList)) {
      throw new ApplicationException(DmsErrorConstant.SEARCH_CRITERIA_NON_EXIST);
    }
  }


  /**
   * Checks the user defined profile that support auto generation or not by 
   * dmsDocumentDetail object. 
   * 
   * @param docDetail the dmsDocumentDetail object for checking its correponding user defined
   *                  profile support auto generation
   * @return          true if auto generation is support.
   * @throws ApplicationException
   */
  public boolean validateUDFAutoGeneration(DmsDocumentDetail docDetail) throws ApplicationException{
    boolean supportAutoGen = false;
    SysUserDefinedIndexDAObject udfDAO = new SysUserDefinedIndexDAObject(sessionContainer, conn);
    SysUserDefinedIndex udf = (SysUserDefinedIndex) udfDAO.getObjectByID(docDetail.getUserDefinedFieldID());
    if (udf !=null && !Utility.isEmpty(udf.getGenKeyTemplate())){
      supportAutoGen = true;
    }
    udfDAO=null;
    return supportAutoGen;
  }

  /*public void checkStorageLimitAvailable(DmsVersion dmsVersion) throws DuplicateNameException, ApplicationException {
    DocumentRetrievalManager docRetrievalManager = new DocumentRetrievalManager(sessionContainer, conn);
    DmsDocument dmsDocument = (DmsDocument)docRetrievalManager.getDocument(dmsVersion.getDocumentID());
    dmsDocument.setItemSize();
    checkStorageLimitAvailable(dmsDocument);
  }*/

  
  
  /**
   * Checks there are free space to add a new document under the root.
   * ApplicationException DmsErrorConstant.NO_AVAILABLE_SPACE is thrown when
   * not space is available.
   * 
   * @param dmsDocument 
   * @throws DuplicateNameException
   * @throws ApplicationException 
   * @see com.dcivision.dms.DmsErrorConstant
   * @see com.dcivision.dms.core.RootRetrievalManager
   * @see com.dcivision.dms.core.DocumentOperationManager
   **/
  public void checkStorageLimitAvailable(DmsDocument dmsDocument) throws DuplicateNameException, ApplicationException {
    RootRetrievalManager rootRetrievalManager = new RootRetrievalManager(sessionContainer, conn);

    //Check for the space available on document only
    if (DmsDocument.DOCUMENT_TYPE.equals(dmsDocument.getDocumentType()) || 
        DmsDocument.COMPOUND_DOC_TYPE.equals(dmsDocument.getDocumentType()) ||
        DmsDocument.EMAIL_DOC_TYPE.equals(dmsDocument.getDocumentType()) ||
        DmsDocument.FORM_DOC_TYPE.equals(dmsDocument.getDocumentType()) ||
        DmsDocument.FLOW_DOC_TYPE.equals(dmsDocument.getDocumentType())) {
      DmsRoot masterRoot = rootRetrievalManager.getRootObject(dmsDocument.getRootID());
      //log.debug("root folder ID = " + dmsDocument.getRootID() + "storage Limite = " + masterRoot.getStorageLimit());
      Double totalSize = masterRoot.getStorageLimit(); //Size in MB
      Double usedSpace = rootRetrievalManager.getUsedSpace(dmsDocument.getRootID()); //Size in byte

      if (totalSize.doubleValue()>0.0){
          double newSpaceInMB = (usedSpace.doubleValue() + dmsDocument.getItemSize().doubleValue()) / 1024.0 / 1024.0;
          log.debug("total space (MB) = " + totalSize);
          log.debug("total usedSpace (byte) = " + usedSpace);
          log.debug("total newSpaceInMB (MB) = " + newSpaceInMB);
          if (newSpaceInMB > totalSize.doubleValue()) {
              throw new ApplicationException(DmsErrorConstant.NO_AVAILABLE_SPACE);
          }
      }
    }
    rootRetrievalManager.release();
  }
  /**
   * Checks there are free space to add a new document under the root in the workflow progress.
   * ApplicationException DmsErrorConstant.NO_AVAILABLE_SPACE is thrown when
   * not space is available.
   * Call form check in.
   * @param dmsDocument        Current document when chenck in 
   * @throws DuplicateNameException
   * @throws ApplicationException 
   * @see com.dcivision.dms.DmsErrorConstant
   * @see com.dcivision.dms.core.RootRetrievalManager
   * @see com.dcivision.dms.core.DocumentOperationManager
   * @author Bill
   **/  
  public void checkStorageLimitAvailableInWorkflow(DmsDocument dmsDocument) throws DuplicateNameException,ApplicationException{
    MtmWorkflowProgressSystemObjectDAObject mtmWorkflowProgressSystemObjectDAO=new MtmWorkflowProgressSystemObjectDAObject(sessionContainer,conn);
    WorkflowProgressAttachmentDAObject WorkflowProgressAttachmentDAO=new WorkflowProgressAttachmentDAObject(sessionContainer,conn);
    List mtmWorkflowProgressSystemObjectList=mtmWorkflowProgressSystemObjectDAO.getListByBeanClassNameObjectID(DmsDocument.class.getName(), dmsDocument.getID());
    if(!Utility.isEmpty(mtmWorkflowProgressSystemObjectList)){
    	// this document not binded workflow
    	MtmWorkflowProgressSystemObject mtmWorkflowProgressSystemObject =(MtmWorkflowProgressSystemObject)mtmWorkflowProgressSystemObjectList.get(0);
    	List WorkflowProgressAttachmentList=WorkflowProgressAttachmentDAO.getListByWorkflowProgressID(mtmWorkflowProgressSystemObject.getWorkflowProgressID());
    	if(!Utility.isEmpty(WorkflowProgressAttachmentList)){
    	  WorkflowProgressAttachment workflowProgressAttachment =(WorkflowProgressAttachment)WorkflowProgressAttachmentList.get(0);
    	  Integer attachmentSize = workflowProgressAttachment.getContentSize();
    	  Integer totalSize=new Integer(dmsDocument.getItemSize().intValue()+attachmentSize.intValue());
    	  log.debug("total space (MB) = " + totalSize);
          log.debug("total attachmentSize (MB) = " + attachmentSize);
          dmsDocument.setItemSize(totalSize);
    	}
    }
	checkStorageLimitAvailable(dmsDocument);
  }
  /**
   * Check DmsAttachment size with this workflow unload path of public folder size.
   * Call form workflow task.
   * @param request
   * @param attachmentFilenames      Current workflow progress DmsAttachment file names.
   * @param currentRecordDmsRootID   Current workflow record setup upload path of public folder root id.
   * @throws ApplicationException
   * @author Bill
   */
  public void checkDmsAttachmentWithPublicFolderSize(String attachmentFilenames,Integer currentRecordDmsRootID) throws ApplicationException{
    DmsDocument dummyDmsDocument=new DmsDocument();
    dummyDmsDocument.setDocumentType(DmsDocument.DOCUMENT_TYPE);
    dummyDmsDocument.setRootID(currentRecordDmsRootID);
    log.debug("The current progress unload path root ID:"+currentRecordDmsRootID);
    MultipleFileUploadUtil uploadUtil = new MultipleFileUploadUtil(sessionContainer);
    List dmsAttachmentList = uploadUtil.getWorkflowAttachmentList(attachmentFilenames, WorkflowProgressAttachment.ATTACHED_SINGLE_FILE);
    WorkflowProgressAttachment attachment = null;
    if(Utility.isEmpty(dmsAttachmentList)){
      return;
    }
    for(int index=0;index<dmsAttachmentList.size();index++){
      attachment=(WorkflowProgressAttachment)dmsAttachmentList.get(index);
      Integer attachmentSize=attachment.getContentSize();
      log.debug("The current progress unload file size:"+attachmentSize);
      dummyDmsDocument.setItemSize(attachmentSize);
    }
    checkStorageLimitAvailableInWorkflow(dummyDmsDocument);   
  }
  
  /**
   * Check DmsAttachment size with this workflow unload path of public folder size.
   * Call form workflow task.
   * @param attachmentFilenames    Current workflow progress DmsAttachment file names.
   * @param documentId             Current workflow record setup upload path of public folder document id.
   * @throws ApplicationException
   */
  public void checkDmsAttachmentWithPublicFolderSizeByDocumentID(String attachmentFilenames,Integer documentId) throws ApplicationException{
    Integer currentRecordDmsRootID = null; 
    
    if(attachmentFilenames==null||documentId==null){
      return;
    }    
    currentRecordDmsRootID = getDocumentRootIDByDocumentID(documentId);
    checkDmsAttachmentWithPublicFolderSize(attachmentFilenames, currentRecordDmsRootID);    
  }
  
  /**
   * get document rootID by documentID from DmsDocumentDAObject
   * @param documentId   DmsDocument table's  id
   * @return dmsRootID   DmsDocument table's  rootId
   */
  public Integer getDocumentRootIDByDocumentID(Integer documentId){
    Integer dmsRootID = null;
    DmsDocumentDAObject dmsDocDAO = new DmsDocumentDAObject(sessionContainer, conn); 
    DmsDocument document = null;
    
    try {
      document = (DmsDocument)dmsDocDAO.getDocumentByID(documentId,GlobalConstant.RECORD_STATUS_ACTIVE);      
    } catch (ApplicationException exp) {
      log.error("Error info", exp);
    }
    if(document!=null){
      dmsRootID = document.getRootID();
    }
    return dmsRootID;
  }  
  
  /**
   * Checks the data inputStream is empty or not.
   * It is mainly use for the creating document and checking document for checking.
   * EmptyContentException is thrown when the inputStream data is empty
   * 
   * @param data InputStream data of the dmsDocument object
   * @throws EmptyContentException
   * @throws ApplicationException
   * @see com.dcivision.dms.core.DocumentOperationManager
   */
  public void checkDocumentIsEmptyContent(InputStream data) throws EmptyContentException, ApplicationException {
    try{
      if (data!=null && data.available()<=0) {
        throw new EmptyContentException(DmsErrorConstant.EMPTY_CONTENT_DOCUMENT);
      }
    } catch (IOException ioe) {
      log.error(ioe, ioe);
    }
  }


  /**
   * Checks source document is the parent of the destination document or not.
   * <p>
   * When copy or move a folder, compound document or paper document to a destination folder
   * The source document (folder, compound document or paper document) cannot move or copy under
   * its sub documents (folder, compound or paper document)
   * <p>
   *  
   * @param sourceDocument the source document
   * @param destinationDoc the destination document
   * @throws DuplicateNameException
   * @throws ApplicationException
   * @see com.dcivision.dms.core.DocumentOperationManager
   */
  public void checkUnderSameNode(DmsDocument sourceDocument, DmsDocument destinationDoc) throws DuplicateNameException, ApplicationException {
    DocumentRetrievalManager docRetrievalManager = new DocumentRetrievalManager(sessionContainer, conn);
    boolean checkUnderSameNode = false;  //used to check the sourceDocument and the destinationDoc is under same node
    int sourceDocID = sourceDocument.getID().intValue();
    int parentID = destinationDoc.getParentID().intValue();  //the destination's id
    DmsDocument parentDoc = destinationDoc;
    while (parentID!=0){
      if (sourceDocID==parentID){
        throw new ApplicationException(DmsErrorConstant.UNDER_SAME_NODE);
      }else{
        parentDoc = docRetrievalManager.getDocument(parentDoc.getParentID());
        parentID = parentDoc.getParentID().intValue();
      }
    }
    docRetrievalManager.release();
  }

  
  /**
   * Checks whether has permission of the object with specified permission
   * 
   * <P>
   * It is mainly for checking the whether has permission on the document with specified
   * permission type. If the object (dmsDocument) is under personal folder of current user
   * ,it always has permission.
   * 
   * @param objectType the object type
   * @param objectID   the object ID
   * @param rootID     the root ID
   * @param permissionCode  the specified permission type for checking
   * @return
   * @throws ApplicationException
   */
  public boolean checkHasPermissionOnObject(String objectType, Integer objectID, Integer rootID, String permissionCode) throws ApplicationException {
    boolean havePermission = false;
    PermissionManager permissionManager = sessionContainer.getPermissionManager();
    DocumentRetrievalManager docRetrievalManager = new DocumentRetrievalManager(sessionContainer, conn);
    RootRetrievalManager rootManager = new RootRetrievalManager(sessionContainer, conn);
    DmsRoot root = rootManager.getRootObject(rootID);
    if (root==null){
      throw new ApplicationException(DmsErrorConstant.ROOT_LOC_NOT_FOUND);
    }
    if (DmsRoot.PERSONAL_ROOT.equals(root.getRootType())) { //Personal folder can do everything
      havePermission = true;
    }else{

      boolean hasRight = false;
      String permission = null;
      DmsDocument dmsObj = null;
      if (objectID.intValue() != 0) {
        dmsObj = docRetrievalManager.getDocument(objectID);
        try {
          permission = permissionManager.getPermission(conn,objectType, objectID);
        } catch (ApplicationException appEx) {
          log.error(appEx, appEx);
          permission = "";
        }

        log.debug(" objectID: " + objectID + ", permission: " + permission);

        if (permission.indexOf(permissionCode) < 0) {
          String actionTryToCarryOut = MessageResourcesFactory.getMessage(sessionContainer.getSessionLocale(), "dms.label.permission_" + permissionCode);
          throw new ApplicationException(DmsErrorConstant.HAVE_NO_FUNCTION_PERMISSION, null, actionTryToCarryOut);
        }
      }
      if (objectID.intValue() == 0) {
        try {
          dmsObj = docRetrievalManager.getRootFolderByRootID(rootID);
          permission = permissionManager.getPermission(conn,objectType, dmsObj.getID());
        } catch (ApplicationException appEx) {
          log.error(appEx, appEx);
          permission = "";
        }

        log.debug(" ID: " + dmsObj.getID() + ", permission: " + permission);
        if (permission.indexOf(permissionCode) < 0) {
          String actionTryToCarryOut = MessageResourcesFactory.getMessage(sessionContainer.getSessionLocale(), "dms.label.permission_" + permissionCode);
          throw new ApplicationException(DmsErrorConstant.HAVE_NO_FUNCTION_PERMISSION, null, actionTryToCarryOut);
        }
      }
    }
    docRetrievalManager.release();
    rootManager.release();
    return true;
  }

  /**
   * Checks any already defined document relationship with the same relationship type
   * ApplicationException DmsErrorConstant.DUPLICATE_DOC_RELATIONSHIP is throw if there are 
   * same relation already defined.
   * 
   * @param documentID          the document id 
   * @param relatedDocumentID   the target document id that has relationship to document
   * @param relationType        the relation type
   * @return true if no duplicate relation
   * @throws ApplicationException
   * @see com.dcivision.dms.DmsErrorConstant
   */
  public boolean checkDuplicateRelation(Integer documentID, Integer relatedDocumentID, String relationType) throws  ApplicationException {
    MtmDocumentRelationshipDAObject relationDAO = new MtmDocumentRelationshipDAObject(sessionContainer, conn);
    //check there is exist document relation, true is has exist

    if(relationDAO.checkExistRelation(documentID,relatedDocumentID,relationType)){
      throw new ApplicationException(DmsErrorConstant.DUPLICATE_DOC_RELATIONSHIP);
    }
    relationDAO = null;
    return true;

  }




  /**
   * Checks the invalid chars for adavance, bulk search by using lucene type searching.
   * 
   * @param searchForm the form object contains the search criteria
   * @throws ApplicationException
  */
  public void validateLuceneSearchStr(ListDmsDocumentForm searchForm ) throws ApplicationException{

    String invalidChar = "\"";
    if(!Utility.isEmpty(searchForm.getDocumentName())){
      if ( (searchForm.getDocumentName().indexOf(invalidChar) >= 0)) {
        throw new ApplicationException(ErrorConstant.SYS_CHARACTER_ERROR, null, "\"");
      }
    }

    if(!Utility.isEmpty(searchForm.getReferenceNo())){
      if( (searchForm.getReferenceNo().indexOf(invalidChar) >= 0)){
        throw new ApplicationException(ErrorConstant.SYS_CHARACTER_ERROR, null, "\"");
      }
    }

    if(!Utility.isEmpty(searchForm.getDescription())){
      if ( (searchForm.getDescription().indexOf(invalidChar) >= 0)) {
        throw new ApplicationException(ErrorConstant.SYS_CHARACTER_ERROR, null, "\"");
      }
    }

    if(!Utility.isEmpty(searchForm.getUserDef1())){
      if( (searchForm.getUserDef1().indexOf(invalidChar) >= 0)){
        throw new ApplicationException(ErrorConstant.SYS_CHARACTER_ERROR, null, "\"");
      }
    }

    if(!Utility.isEmpty(searchForm.getUserDef2())){
      if( (searchForm.getUserDef2().indexOf(invalidChar) >= 0)){
       throw new ApplicationException(ErrorConstant.SYS_CHARACTER_ERROR, null, "\"");
      }
    }

   if(!Utility.isEmpty(searchForm.getUserDef3())){
     if( (searchForm.getUserDef3().indexOf(invalidChar) >= 0)){
      throw new ApplicationException(ErrorConstant.SYS_CHARACTER_ERROR, null, "\"");
     }
   }

   if(!Utility.isEmpty(searchForm.getUserDef4())){
     if( (searchForm.getUserDef4().indexOf(invalidChar) >= 0)){
      throw new ApplicationException(ErrorConstant.SYS_CHARACTER_ERROR, null, "\"");
     }
   }

   if(!Utility.isEmpty(searchForm.getUserDef5())){
     if( (searchForm.getUserDef5().indexOf(invalidChar) >= 0)){
      throw new ApplicationException(ErrorConstant.SYS_CHARACTER_ERROR, null, "\"");
     }
   }

   if(!Utility.isEmpty(searchForm.getUserDef6())){
     if( (searchForm.getUserDef6().indexOf(invalidChar) >= 0)){
      throw new ApplicationException(ErrorConstant.SYS_CHARACTER_ERROR, null, "\"");
     }
   }

   if(!Utility.isEmpty(searchForm.getUserDef7())){
     if( (searchForm.getUserDef7().indexOf(invalidChar) >= 0)){
      throw new ApplicationException(ErrorConstant.SYS_CHARACTER_ERROR, null, "\"");
     }
   }

   if(!Utility.isEmpty(searchForm.getUserDef8())){
     if( (searchForm.getUserDef8().indexOf(invalidChar) >= 0)){
       throw new ApplicationException(ErrorConstant.SYS_CHARACTER_ERROR, null, "\"");
     }
   }

   if(!Utility.isEmpty(searchForm.getUserDef9())){
     if( (searchForm.getUserDef9().indexOf(invalidChar) >= 0)){
       throw new ApplicationException(ErrorConstant.SYS_CHARACTER_ERROR, null, "\"");
     }
   }

   if(!Utility.isEmpty(searchForm.getUserDef10())){
     if( (searchForm.getUserDef10().indexOf(invalidChar) >= 0)){
       throw new ApplicationException(ErrorConstant.SYS_CHARACTER_ERROR, null, "\"");
     }
   }

   /* search the userdefine field */
  if (!Utility.isEmpty(searchForm.getUserDefinedFieldID())) {
    List docDetailList = searchForm.getDocumentDetails();
    DmsDocumentDetail docDetail  = new DmsDocumentDetail();

    /* search each udf criteria */
      for(int i=0;i<docDetailList.size();i++){
        docDetail = (DmsDocumentDetail) docDetailList.get(i);

        if (!Utility.isEmpty(docDetail.getFieldValue()) || docDetail.getDateValue() != null ||
           docDetail.getToDateValue() != null || docDetail.getNumericValue() != null ||
           docDetail.getToNumericValue() != null) {
           //String searchField ="UDF_" + docDetail.getUserDefinedFieldDetailID()+"_"+docDetail.getFieldType();

           if ((SysUserDefinedIndexDetail.STRING_FIELD.equals(docDetail.
            getFieldType()) || docDetail.getFieldType().startsWith("CUST")) && !Utility.isEmpty(docDetail.getFieldValue())) {
             /* string value */
             if( (docDetail.getFieldValue().indexOf(invalidChar) >= 0)){
               throw new ApplicationException(ErrorConstant.SYS_CHARACTER_ERROR, null, "\"");
             }

           }




       }//end field empty if

      }//end for

    }//end detail list if





  }

  /**
   * Gets the items status that the expired document is seen by owner or non-
   * owner user.
   * <P>
   * if the document is expired and the SYS_PARAMETER dms.show_expire_doc
   *                               is set to true, if the user is not the owner of the document, it only
   *                               allow to read document, no other action. The action the user can take 
   *                               is like checkout mode of the document.
   * <P>
   *    @param    dmsDocument
   * @return itemStatus of the document, DmsVersion.EXCLUSIVE_LOCK "X", if user is not 
   *                               owner of document
   * @throws ApplicationException
   */
  public String checkExpireDocumentOwner(DmsDocument dmsDocument) throws ApplicationException{
    java.sql.Timestamp nowTime = Utility.getCurrentTimestamp();
    java.sql.Timestamp oneDayBeforeCurrTime = Utility.addDay(nowTime, -1);
    String itemStatus = dmsDocument.getItemStatus();
    boolean isExpire = true;
    if(SystemParameterFactory.getSystemParameterBoolean(SystemParameterConstant.DMS_SHOW_EXPIRE_DOC)){
   

      if (dmsDocument.getEffectiveStartDate()==null && dmsDocument.getEffectiveEndDate()==null) {
        isExpire=false;
      }

      if ((dmsDocument.getEffectiveStartDate()==null || dmsDocument.getEffectiveStartDate().equals(nowTime) || dmsDocument.getEffectiveStartDate().before(nowTime))
           && (dmsDocument.getEffectiveEndDate()==null || dmsDocument.getEffectiveEndDate().equals(nowTime) || dmsDocument.getEffectiveEndDate().after(oneDayBeforeCurrTime))) {
        isExpire=false;
      }
      
      if(dmsDocument.getEffectiveStartDate().getTime() >= nowTime.getTime() ){
        isExpire = false;
      }      
     
      if(isExpire){     
        if(this.sessionContainer.getUserRecordID().intValue()!=dmsDocument.getOwnerID().intValue()){
          itemStatus = DmsVersion.EXCLUSIVE_LOCK;
        }
      }

    }  
    
    return itemStatus;
  }
  
  /**
   * Checks the document is expired and the current user is the owner of the document.
   * 
   * @param dmsDocument the dmsDocument object to check the expired and current user 
   *                    is the owner or not
   * @return true if the document is expired and the current user is not its owner
   * @throws ApplicationException
   */
  public boolean checkExpireDocumentOwnerStatus(DmsDocument dmsDocument) throws ApplicationException{
    java.sql.Timestamp nowTime = Utility.getCurrentTimestamp();
    java.sql.Timestamp oneDayBeforeCurrTime = Utility.addDay(nowTime, -1);
    boolean isExpireAndNotOwner = false;
    boolean isExpire = true;
    if(SystemParameterFactory.getSystemParameterBoolean(SystemParameterConstant.DMS_SHOW_EXPIRE_DOC)){
   

     if (dmsDocument.getEffectiveStartDate()==null && dmsDocument.getEffectiveEndDate()==null) {
       isExpire=false;
     }

     if ((dmsDocument.getEffectiveStartDate()==null || dmsDocument.getEffectiveStartDate().equals(nowTime) || dmsDocument.getEffectiveStartDate().before(nowTime))
          && (dmsDocument.getEffectiveEndDate()==null || dmsDocument.getEffectiveEndDate().equals(nowTime) || dmsDocument.getEffectiveEndDate().after(oneDayBeforeCurrTime))) {
       isExpire=false;
     }
     
     if(dmsDocument.getEffectiveStartDate().getTime() >= nowTime.getTime() ){
       isExpire = false;
     }
     
     
     if(isExpire){     
       if(this.sessionContainer.getUserRecordID().intValue()!=dmsDocument.getOwnerID().intValue()){
         isExpireAndNotOwner = true;
       }
     }
    
   }
   return isExpireAndNotOwner;  
  }
  
/**
 *  check the document whether is expired according to owner.
 * @param dmsDocument
 * @return
 */
  public boolean checkExpireDocumentByOwner(DmsDocument dmsDocument) {
    Timestamp nowTime = Utility.getCurrentTimestamp();
    Timestamp oneDayBeforeCurrTime = Utility.addDay(nowTime, -1);
    boolean isExpireAndNotOwner = false;
    boolean isExpire = true;
    try {
     if (dmsDocument.getEffectiveStartDate()==null && dmsDocument.getEffectiveEndDate()==null) {
       isExpire=false;
     }

     if ((dmsDocument.getEffectiveStartDate()==null || dmsDocument.getEffectiveStartDate().equals(nowTime) || dmsDocument.getEffectiveStartDate().before(nowTime))
          && (dmsDocument.getEffectiveEndDate()==null || dmsDocument.getEffectiveEndDate().equals(nowTime) || dmsDocument.getEffectiveEndDate().after(oneDayBeforeCurrTime))) {
       isExpire=false;
     }
     
     if(dmsDocument.getEffectiveStartDate().getTime() >= nowTime.getTime() ){
       isExpire = false;
     }
     
     
     if(isExpire){     
       if(this.sessionContainer.getUserRecordID().intValue()!=dmsDocument.getOwnerID().intValue()){
         isExpireAndNotOwner = true;
       }
     }
    } catch (Exception e) {
      log.error(e);
      return false;
    }
   return isExpireAndNotOwner;  
  }
  /**
   * Checks the physical content is shared by other dmsDocument object.
   *    
   * <P>
   * If the document is copied before,the physical content will be share, 
   * for hard delete, it must check whether share is exist
   * <P>
   * @param content the DmsContent object
   * @param locMasterID the location master ID (physical file location)
   * @return true if the physical file is share by 2 or more documents
   * @throws ApplicationException
   * @see com.dcivision.dms.bean.DmsContent
   */
  public boolean checkSharePhysicalDoc(DmsContent content, Integer locMasterID) throws ApplicationException{
    boolean isShare= false;
    DmsContentDAObject dmsContentDAO = new DmsContentDAObject(sessionContainer, conn);
    DmsRootDAObject dmsRootDAO = new DmsRootDAObject(sessionContainer, conn);
    List shareContentList = new java.util.ArrayList();
    try{
    
    String converted = content.getConvertedName()== null? "":content.getConvertedName();
    if(!Utility.isEmpty(converted)&& !Utility.isEmpty(locMasterID)){
      shareContentList = dmsContentDAO.getListByConvertedNameUnderSameLoc(converted, locMasterID);
      if(shareContentList.size()>0){
        isShare = true;
      }
    }
    
    }catch(Exception e){
      log.error(e, e);
    }

    return isShare;
    
  }
  
  /**
   * Checks the document is expired and the current user is the owner of the document.
   * <P>
   * This method is mainly for invitation case to check the dmsDocument invited is expired
   * and the user is the owner of the document
   * <P>
   * 
   * @param dmsDocument the dmsDocument object to check the expired and current user 
   *                    is the owner or not
   * @return true if the document is expired and the current user is not its owner
   * @throws ApplicationException
   */
  public boolean checkInvitationExpireDocumentOwnerStatus(DmsDocument dmsDocument) throws ApplicationException{
    java.sql.Timestamp nowTime = Utility.getCurrentTimestamp();
    java.sql.Timestamp oneDayBeforeCurrTime = Utility.addDay(nowTime, -1);
    boolean isExpireAndNotOwner = false;
    boolean isExpire = true;

    if (dmsDocument.getEffectiveStartDate()==null && dmsDocument.getEffectiveEndDate()==null) {
      isExpire=false;
    }

    if ((dmsDocument.getEffectiveStartDate()==null || dmsDocument.getEffectiveStartDate().equals(nowTime) || dmsDocument.getEffectiveStartDate().before(nowTime))
        && (dmsDocument.getEffectiveEndDate()==null || dmsDocument.getEffectiveEndDate().equals(nowTime) || dmsDocument.getEffectiveEndDate().after(oneDayBeforeCurrTime))) {
       isExpire=false;
    }
     
    if(dmsDocument.getEffectiveStartDate().getTime() >= nowTime.getTime() ){
      isExpire = false;
    }
     
    if(isExpire){     
      if(this.sessionContainer.getUserRecordID().intValue()!=dmsDocument.getOwnerID().intValue()){
        isExpireAndNotOwner = true;
      }
    }
    
    return isExpireAndNotOwner;  
  }
  
  /**
   * for check permission to shw the workflow icon
   * @param wfRecordGroupID
   * @return
   * @throws ApplicationException
   */
  public boolean checkPermissionToShowWfIcon(Integer wfRecordGroupID) throws ApplicationException{
    boolean permission = false;
    PermissionManager pm = sessionContainer.getPermissionManager();
    String permissionStr = pm.getPermission(GlobalConstant.OBJECT_TYPE_WORKFLOW, wfRecordGroupID);
    if(permissionStr.indexOf("R")>=0){
      permission = true;
    }
    return permission;
  }
  
  /**
   * Return available free disk space for the gived disk drive.
   * Only can use in windows operation system, unix will return -1
   * lee.lv add in06/06/19
   * @param dirName
   * @return
   * @throws ApplicationException
   */
  public long getFreeDiskSpace(String dirName) throws ApplicationException {
    try {
      // operating system name
      String os = System.getProperty("os.name");
      String dirDriver = dirName.indexOf(":")!=-1 ? dirName.substring(0, dirName.indexOf(":")+1) : dirName;
      String command;
      if (os.equals("Windows NT") || os.equals("Windows 2000") || os.equals("Windows XP") ||  os.equals("Windows 2003")) {
        command = "cmd.exe /c dir " + dirDriver;
      //} else if (os.indexOf("Windows")!=-1) {
        //command = "command.com /c dir " + dirDriver;
      }else {
        //not window operation system, can't get the free disk space
        return -1;
      }
      // run the dir command on the argument directory name
      Runtime runtime = Runtime.getRuntime();
      Process process = null;
      process = runtime.exec(command);
      if (process == null) {
        return -1;
      }
      // read the output of the dir command
      // only the last line is of interest
      BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
      String line;
      String freeSpace = null;
      while ((line = in.readLine()) != null) {
        freeSpace = line;
      }
      if (freeSpace == null) {
        return -1;
      }
      process.destroy();
      // remove dots & commas & leading and trailing whitespace
      freeSpace = freeSpace.trim();
      freeSpace = freeSpace.replaceAll("\\.", "");
      freeSpace = freeSpace.replaceAll(",", "");
      String[] items = freeSpace.split(" ");
      // the first valid numeric value in items after(!) index 0
      // is probably the free disk space
      int index = 1;
      while (index < items.length) {
        try {
          long bytes = Long.parseLong(items[index++]);
          return bytes;
        } catch (NumberFormatException nfe) {
        }
      }
      return -1;
    } catch (Exception exception) {
      return -1;
    }
  } 
  
  /**
   * get total item size by document id list
   * it will count all versions' item size
   * lee.lv add
   * @param documentIds
   * @return
   * @throws ApplicationException
   */
  public long getItemSizeTotal(List documentIds, boolean isTopVersion) throws ApplicationException {
    DmsVersionDAObject dmsVersionDAO = new DmsVersionDAObject(sessionContainer, conn);
    long sizeTotal = 0;
    int batchSize = 100;
    int batchIndex = 0;
    int size = documentIds.size();
    while (true) {
      int index_start = (batchIndex++) * batchSize;
      if (index_start>=size) { break; }
      int index_end = (index_start+batchSize)>size ? size : (index_start+batchSize);
      List batchDocumentIds = documentIds.subList(index_start, index_end);
      
      sizeTotal += dmsVersionDAO.getItemSizeTotal(batchDocumentIds, isTopVersion).longValue();
    }
    return sizeTotal;
  }

  /**
   * validate before copy action lee.lv add
   * 
   * @param sourceDocument
   * @param destinDocument
   * @param newDmsDocument
   * @param options
   * @return
   * @throws ApplicationException
   */
  public DmsValidation validateCopyAction(DmsDocument sourceDocument, DmsDocument destinDocument, DmsDocument newDmsDocument,
      boolean[] options) throws ApplicationException {
    DmsDocumentDAObject dmsDocumentDAO = new DmsDocumentDAObject(this.sessionContainer, conn);
    DocumentRetrievalManager docRetrievalManager = new DocumentRetrievalManager(sessionContainer, conn);
    MtmDocumentRelationshipDAObject mtmRelationDAO = new MtmDocumentRelationshipDAObject(sessionContainer, conn);
    DmsRootDAObject dmsRootDAO = new DmsRootDAObject(this.sessionContainer, conn);
    PermissionManager permissionManager = sessionContainer.getPermissionManager();
    DmsValidation validation = new DmsValidation();
    List lstIdHasRight = new ArrayList(); // node ids has delete right
    List lstIdMisRight = new ArrayList(); // node ids has not delete right
    List lstIdBeLocked = new ArrayList(); // node ids has been locked by user
    List lstIdHaveRelationship = new ArrayList();
    boolean shortcutFromPersonalToPublic = false;
    // step:validate shortcut copy
    if (DmsDocument.DOCUMENT_LINK.equals(sourceDocument.getDocumentType())) {
      this.validateNewShortcutAction(sourceDocument, destinDocument);
    }

    try {
      TextUtility.stringValidation(newDmsDocument.getDocumentName());
    } catch (Exception ex) {
      validation.setInvalidName(true);
      validation.setSuccess(false);
      return validation;
    }

    // step 1: validate is under same node
    try {
      this.checkUnderSameNode(sourceDocument, destinDocument);
    } catch (Exception ex) {
      validation.setUnderSameNode(true);
      validation.setSuccess(false);
      return validation;
    }
    // step 2: validate is duplicate name in same tree level
    try {
      DmsDocument tmpSourceDocument = (DmsDocument) sourceDocument.clone();
      tmpSourceDocument.setDocumentName(newDmsDocument.getDocumentName());
      tmpSourceDocument.setParentID(destinDocument.getID());
      tmpSourceDocument.setRootID(destinDocument.getRootID());
      if (this.checkAndLockDuplicateNameCache(tmpSourceDocument) == false) { // EIP-1444
                                                                              // 07/01/10
                                                                              // LEE
                                                                              // if
                                                                              // (this.checkDuplicateNameSupport(tmpSourceDocument)==false)
                                                                              // {
        throw new DuplicateNameException(DmsErrorConstant.DUPLICATE_FILE_NAME);
      }
    } catch (Exception ex) {
      validation.setDuplicateName(true);
      validation.setSuccess(false);
      return validation;
    }

    // step 3: validate permission/locked/or something else
    DmsRoot dmsRootS = (DmsRoot) dmsRootDAO.getObjectByID(sourceDocument.getRootID());
    DmsRoot dmsRootD = (DmsRoot) dmsRootDAO.getObjectByID(destinDocument.getRootID());
    if (DmsRoot.PERSONAL_ROOT.equals(dmsRootD.getRootType())) {
      validateShortCut(docRetrievalManager, mtmRelationDAO, dmsRootDAO, lstIdHaveRelationship, sourceDocument.getID(), false,destinDocument);
    }
    boolean hasPermissionD = DmsRoot.PERSONAL_ROOT.equals(dmsRootD.getRootType())
        || permissionManager
            .hasAccessRight(
                this.conn,
                GlobalConstant.OBJECT_TYPE_DOCUMENT,
                destinDocument.getID(),
                dmsRootD.getRootType(),
                dmsRootD.getOwnerID(),
                ((DmsDocument.DOCUMENT_TYPE.equals(sourceDocument.getDocumentType())||DmsDocument.DOCUMENT_LINK.equals(sourceDocument.getDocumentType())) ? WebdavOperationManager.DMS_PERMISSION_CREATE_DOCUMENT_CODE
                    : WebdavOperationManager.DMS_PERMISSION_CREATE_FOLDER_CODE));
    boolean hasPermissionS = DmsRoot.PERSONAL_ROOT.equals(dmsRootS.getRootType())
        || permissionManager.hasAccessRight(this.conn, GlobalConstant.OBJECT_TYPE_DOCUMENT, sourceDocument.getID(), dmsRootS.getRootType(),
            dmsRootS.getOwnerID(), WebdavOperationManager.DMS_PERMISSION_COPY_CODE);
    boolean hasBeenLockedS = GlobalConstant.STATUS_LOCKED.equals(sourceDocument.getItemStatus());
    if (!hasPermissionD || !hasPermissionS || hasBeenLockedS) {
      if (!hasPermissionD) {
        lstIdMisRight.add(destinDocument.getID());
      }
      if (!hasPermissionS) {
        lstIdMisRight.add(sourceDocument.getID());
      }
      if (hasBeenLockedS) {
        lstIdBeLocked.add(sourceDocument.getID());
      }
    } else {
      lstIdHasRight.add(sourceDocument.getID());

      String rootType = dmsRootS.getRootType();
      Integer rootOwnerId = dmsRootS.getOwnerID();
      String[] recordStatus = new String[] { GlobalConstant.RECORD_STATUS_ACTIVE };
      String[] documentTypes = options[3] ? new String[] { DmsDocument.FOLDER_TYPE } : null;

      // step 5: validate children folders or documents
      List documentIds = dmsDocumentDAO.getIDListByParentID(sourceDocument.getID(), documentTypes, recordStatus);
      while (documentIds != null && !documentIds.isEmpty()) {
        // step 5.1: append all children permission

        // step 5.2: validate 100 record everytime
        int batchSize = 100;
        int batchIndex = 0;
        int size = documentIds.size();
        List folderIds = new ArrayList(); // restore folder id for next loop
        while (true) {
          int index_start = (batchIndex++) * batchSize;
          if (index_start >= size) {
            break;
          }
          int index_end = (index_start + batchSize) > size ? size : (index_start + batchSize);
          List batchDocumentIds = documentIds.subList(index_start, index_end);

          permissionManager.appendPermissionCache(conn, batchDocumentIds, GlobalConstant.OBJECT_TYPE_DOCUMENT, rootType, rootOwnerId);

          // step 5.2.1: validate
          String[] fieldNames = new String[] { "A.DOCUMENT_TYPE", "A.ITEM_STATUS", "A.EFFECTIVE_START_DATE", "A.EFFECTIVE_END_DATE" };
          String[] fieldTypes = new String[] { "String", "String", "Timestamp", "Timestamp" };
          Map mapFields = dmsDocumentDAO.getMapIdToFieldValueArray(batchDocumentIds, fieldNames, fieldTypes);
          for (int i = 0; i < batchDocumentIds.size(); i++) {
            Integer docId = (Integer) batchDocumentIds.get(i);
            Object[] fields = (Object[]) mapFields.get(batchDocumentIds.get(i));
  
            if ((DmsRoot.PERSONAL_ROOT.equals(rootType) || permissionManager.hasAccessRight(this.conn, GlobalConstant.OBJECT_TYPE_DOCUMENT,
                docId, rootType, rootOwnerId, "R"))
                && !DmsVersion.ARCHIVED_STATUS.equals(fields[1])) {
              boolean failure = false;
              if (!DmsRoot.PERSONAL_ROOT.equals(rootType)
                  && !permissionManager.hasAccessRight(this.conn, GlobalConstant.OBJECT_TYPE_DOCUMENT, docId, rootType, rootOwnerId,
                      WebdavOperationManager.DMS_PERMISSION_COPY_CODE)) {
                lstIdMisRight.add(docId);
                failure = true;
              }
              if(DmsRoot.PERSONAL_ROOT.equals(dmsRootD.getRootType())) {
                failure = validateShortCut(docRetrievalManager, mtmRelationDAO, dmsRootDAO, lstIdHaveRelationship, docId, failure,destinDocument);
              }           
              if(DmsDocument.DOCUMENT_LINK.equals(fields[0]) && DmsRoot.PERSONAL_ROOT.equals(rootType)) {
                DmsDocument tmp = docRetrievalManager.getRelationshipTargetDocument(docId,DmsDocument.DOCUMENT_LINK);
                if( !Utility.isEmpty(tmp)) {
                  DmsRoot tmpRoot = (DmsRoot)dmsRootDAO.getObjectByID(tmp.getRootID());
                  if( DmsRoot.PERSONAL_ROOT.equals(tmpRoot.getRootType()) && DmsRoot.PUBLIC_ROOT.equals(dmsRootD.getRootType())) {
                    failure = true;
                    shortcutFromPersonalToPublic = true;
                  }
                }
              }
              if (GlobalConstant.STATUS_LOCKED.equals(fields[1])) {
                lstIdBeLocked.add(docId);
                failure = true;
              }

              if (!failure) {
                lstIdHasRight.add(docId);
                if (!DmsDocument.DOCUMENT_TYPE.equals(fields[0]) && !DmsDocument.DOCUMENT_LINK.equals(fields[0])) {
                  folderIds.add(docId);
                }
              }
            }
          }
        }
        documentIds = null;
        if (!folderIds.isEmpty()) {
          documentIds = dmsDocumentDAO.getIDListByParentIDList(folderIds, documentTypes, recordStatus);
        }
      }
    }

    // step: validate freeDiskSpace(only windows operation system)
    if (lstIdHasRight != null && !lstIdHasRight.isEmpty()) {
      long freeDiskSpace = this.getFreeDiskSpace(SystemParameterFactory.getSystemParameter(SystemParameterConstant.DMS_ARCHIVE_FILE_PATH));
      long itemSizeTotal = this.getItemSizeTotal(lstIdHasRight, !options[2]);
      if (freeDiskSpace != -1 && freeDiskSpace < itemSizeTotal) {
        validation.setSuccess(false);
        validation.setNotEnoughDiskSpace(true);
        validation.setDiskSpaceFree(freeDiskSpace);
        validation.setDiskSpaceRequire(itemSizeTotal);
        return validation;
      }
      // validate setted storage limit
      Double settedLimit = ((DmsRoot) dmsRootDAO.getObjectByID(destinDocument.getRootID())).getStorageLimit();
      if (settedLimit != null && settedLimit.doubleValue() > 0.0) {
        Double usedSpace = dmsRootDAO.getUsedSpaceByRootID(destinDocument.getRootID());
        if ((settedLimit.doubleValue() * 1024 * 1024) < (usedSpace.doubleValue() + itemSizeTotal)) {
          validation.setNotEnoughStorageSpace(true);
          validation.setSuccess(false);
          return validation;
        }
      }
    }

    validation.setLstIdAccept(lstIdHasRight);
    validation.setLstIdReject(lstIdMisRight);
    validation.setLstIdlocked(lstIdBeLocked);
    validation.setLstIdHaveRelationship(lstIdHaveRelationship);
    validation.setCantCreateShortcut(shortcutFromPersonalToPublic);
    validation.setSuccess((lstIdMisRight.isEmpty() && lstIdBeLocked.isEmpty())&&lstIdHaveRelationship.isEmpty() && !shortcutFromPersonalToPublic ? true : false);
    return validation;
  }
  
  /**
   * validate before move action lee.lv add
   * 
   * @param sourceDocument
   * @param destinDocument
   * @param newDmsDocument
   * @return
   * @throws ApplicationException
   */
  public DmsValidation validateMoveAction(DmsDocument sourceDocument, DmsDocument destinDocument, DmsDocument newDmsDocument)
      throws ApplicationException {
    DmsDocumentDAObject dmsDocumentDAO = new DmsDocumentDAObject(this.sessionContainer, conn);
    DmsRootDAObject dmsRootDAO = new DmsRootDAObject(this.sessionContainer, conn);
    PermissionManager permissionManager = sessionContainer.getPermissionManager();
    DmsValidation validation = new DmsValidation();
    MtmDocumentRelationshipDAObject mtmRelationDAO = new MtmDocumentRelationshipDAObject(sessionContainer, conn);
    DocumentRetrievalManager docRetrievalManager = new DocumentRetrievalManager(sessionContainer, conn);
    List lstIdHasRight = new ArrayList(); // node ids has delete right
    List lstIdMisRight = new ArrayList(); // node ids has not delete right
    List lstIdBeLocked = new ArrayList(); // node ids has been locked by user
    List lstIdBeArchived = new ArrayList(); // node ids has been archived
    List lstIdReject = new ArrayList();
    boolean shortcutFromPersonalToPublic = false;
    //List lstIdIsMyFavorite = new ArrayList();
    List lstIdHaveRelationship = new ArrayList();// EIP-1663
    try {
      TextUtility.stringValidation(newDmsDocument.getDocumentName());
    } catch (Exception ex) {
      validation.setInvalidName(true);
      validation.setSuccess(false);
      return validation;
    }

    // step 1: validate is under same node
    try {
      this.checkUnderSameNode(sourceDocument, destinDocument);
    } catch (Exception ex) {
      validation.setUnderSameNode(true);
      validation.setSuccess(false);
      return validation;
    }
    // step 2: validate is duplicate name in same tree level
    try {
      DmsDocument tmpSourceDocument = (DmsDocument) sourceDocument.clone();
      tmpSourceDocument.setDocumentName(newDmsDocument.getDocumentName());
      tmpSourceDocument.setParentID(destinDocument.getID());
      tmpSourceDocument.setRootID(destinDocument.getRootID());
      this.checkAndLockDuplicateNameCache(tmpSourceDocument); // EIP-1444
                                                              // 07/01/10 LEE
    } catch (Exception ex) {
      validation.setDuplicateName(true);
      validation.setSuccess(false);
      return validation;
    }

    DmsRoot dmsRoot = (DmsRoot) dmsRootDAO.getObjectByID(sourceDocument.getRootID());
    DmsRoot dmsRootDestin = (DmsRoot) dmsRootDAO.getObjectByID(destinDocument.getRootID());
    String rootType = dmsRoot.getRootType();
    Integer rootOwnerId = dmsRoot.getOwnerID();
    String rootTypeDestin = dmsRootDestin.getRootType();
    Integer rootOwnerIdDestin = dmsRootDestin.getOwnerID();
    // joan.xiong add code for bug EIP-1663
    if (!DmsDocument.DOCUMENT_LINK.equals(sourceDocument.getDocumentType()) && DmsRoot.PUBLIC_ROOT.equals(dmsRoot.getRootType())
        && DmsRoot.PERSONAL_ROOT.equals(dmsRootDestin.getRootType())) {
      //validate document move operation when it have shortcut
      List lstRelatedId = mtmRelationDAO.getRelatedDocIDByDocID(sourceDocument.getID(), DmsDocument.DOCUMENT_LINK, GlobalConstant.RECORD_STATUS_ACTIVE);
      for (int i = 0; i < lstRelatedId.size(); i++) {
        DmsDocument linkDocument = docRetrievalManager.getDocumentByID((Integer) lstRelatedId.get(i));
        DmsRoot linkDocumentRoot = (DmsRoot) dmsRootDAO.getObjectByID(linkDocument.getRootID());
        if (!linkDocumentRoot.getID().equals(dmsRootDestin.getID())) {
          lstIdHaveRelationship.add(linkDocument.getID());
        }
      }
    } else if (DmsDocument.DOCUMENT_LINK.equals(sourceDocument.getDocumentType())) {
      //validate shortcut move
      this.validateNewShortcutAction(sourceDocument, destinDocument);
    }
    // joan.xiong update code end

    // step 3: validate permission/locked/or something else
    boolean hasPermissionD = DmsRoot.PERSONAL_ROOT.equals(rootTypeDestin)
        || permissionManager
            .hasAccessRight(
                this.conn,
                GlobalConstant.OBJECT_TYPE_DOCUMENT,
                destinDocument.getID(),
                rootTypeDestin,
                rootOwnerIdDestin,
                ((DmsDocument.DOCUMENT_TYPE.equals(sourceDocument.getDocumentType())||DmsDocument.DOCUMENT_LINK.equals(sourceDocument.getDocumentType())) ? WebdavOperationManager.DMS_PERMISSION_CREATE_DOCUMENT_CODE
                    : WebdavOperationManager.DMS_PERMISSION_CREATE_FOLDER_CODE));
    boolean hasPermissionS = DmsRoot.PERSONAL_ROOT.equals(rootType)
        || permissionManager.hasAccessRight(this.conn, GlobalConstant.OBJECT_TYPE_DOCUMENT, sourceDocument.getID(), rootType, rootOwnerId,
            WebdavOperationManager.DMS_PERMISSION_MOVE_CODE);
    boolean hasBeenLockedS = GlobalConstant.STATUS_LOCKED.equals(sourceDocument.getItemStatus());
    boolean hasBeenArchivedS = DmsVersion.ARCHIVED_STATUS.equals(sourceDocument.getItemStatus());
    // joan.xiong add code for bug EIP-1549 by 2007/01/24 deal with My Favorite
    // move validation
    /*
    boolean isMyFavoriteFolderS = false;
    if (DmsRoot.PERSONAL_ROOT.equals(rootType)) {
      RootRetrievalManager rootRetrievalManager = new RootRetrievalManager(sessionContainer, conn);
      DmsDocument rootFolder = rootRetrievalManager.getRootDocument(sourceDocument.getRootID());
      if ("My Favorite".equals(sourceDocument.getDocumentName()) && sourceDocument.getParentID().equals(rootFolder.getID())) {
        isMyFavoriteFolderS = true;
      }
    }*/
    // joan.xiong add code end
    if (!hasPermissionD || !hasPermissionS || hasBeenLockedS || hasBeenArchivedS) {
      if (!hasPermissionD) {
        lstIdMisRight.add(destinDocument.getID());
      }
      if (!hasPermissionS) {
        lstIdMisRight.add(sourceDocument.getID());
      }
      if (hasBeenLockedS) {
        lstIdBeLocked.add(sourceDocument.getID());
      }
      if (hasBeenArchivedS) {
        lstIdBeArchived.add(sourceDocument.getID());
      }
    } else {
      lstIdHasRight.add(sourceDocument.getID());

      // step 6: validate children folders and documents
      List documentIds = dmsDocumentDAO.getIDListByParentID(sourceDocument.getID(), null, null);
      while (documentIds != null && !documentIds.isEmpty()) {
        // step 6.2: validate by batch
        int batchSize = 100;
        int batchIndex = 0;
        int size = documentIds.size();
        List folderIds = new ArrayList(); // restore folder id for next loop
        while (true) {
          int index_start = (batchIndex++) * batchSize;
          if (index_start >= size) {
            break;
          }
          int index_end = (index_start + batchSize) > size ? size : (index_start + batchSize);
          // step 6.2.1: get batch id list
          List batchDocumentIds = documentIds.subList(index_start, index_end);

          permissionManager.appendPermissionCache(conn, batchDocumentIds, GlobalConstant.OBJECT_TYPE_DOCUMENT, rootType, rootOwnerId);

          // step 6.2.2: validate batch document
          List documents = dmsDocumentDAO.getListByIDList(batchDocumentIds);
          for (int i = 0; i < documents.size(); i++) {
            DmsDocument document = (DmsDocument) documents.get(i);
            // only active and pending document would be validate
            if (document.getRecordStatus().equals(GlobalConstant.RECORD_STATUS_ACTIVE)
                || document.getRecordStatus().equals(GlobalConstant.RECORD_STATUS_PENDING)) {
              boolean failure = false;
              if (!DmsRoot.PERSONAL_ROOT.equals(rootType)
                  && !permissionManager.hasAccessRight(this.conn, GlobalConstant.OBJECT_TYPE_DOCUMENT, document.getID(), rootType,
                      rootOwnerId, WebdavOperationManager.DMS_PERMISSION_MOVE_CODE)) {
                lstIdMisRight.add(document.getID());
                failure = true;
              }
              if(DmsRoot.PERSONAL_ROOT.equals(dmsRootDestin.getRootType())) {
                failure = validateShortCut(docRetrievalManager, mtmRelationDAO, dmsRootDAO, lstIdHaveRelationship, document.getID(), failure,destinDocument);
              }
              if(DmsDocument.DOCUMENT_LINK.equals(document.getDocumentType()) && DmsRoot.PERSONAL_ROOT.equals(rootType)) {
                DmsDocument tmp = docRetrievalManager.getRelationshipTargetDocument(document.getID(),DmsDocument.DOCUMENT_LINK);
                if( !Utility.isEmpty(tmp)) {
                  DmsRoot tmpRoot = (DmsRoot)dmsRootDAO.getObjectByID(tmp.getRootID());
                  if( DmsRoot.PERSONAL_ROOT.equals(tmpRoot.getRootType()) && DmsRoot.PUBLIC_ROOT.equals(dmsRootDestin.getRootType())) {
                    failure = true;
                    shortcutFromPersonalToPublic = true;
                  }
                }
              }
              if (GlobalConstant.STATUS_LOCKED.equals(document.getItemStatus())) {
                lstIdBeLocked.add(document.getID());
                failure = true;
              }
              if (DmsVersion.ARCHIVED_STATUS.equals(document.getItemStatus())) {
                lstIdBeArchived.add(document.getID());
                failure = true;
              }
              if (!failure) {
                lstIdHasRight.add(document.getID());
              }
            }

            if (!DmsDocument.DOCUMENT_TYPE.equals(document.getDocumentType())
                && !DmsDocument.DOCUMENT_LINK.equals(document.getDocumentType())) {
              folderIds.add(document.getID());
            }
          }
        }
        // step 6.3: get next level node and loop
        documentIds = null;
        if (!folderIds.isEmpty()) {
          documentIds = dmsDocumentDAO.getIDListByParentIDList(folderIds, null, null);
        }
      }

    }

    if (lstIdHasRight != null && !lstIdHasRight.isEmpty()) {
      long itemSizeTotal = this.getItemSizeTotal(lstIdHasRight, false);
      // validate disk space limit
      if (!dmsRoot.getLocID().equals(dmsRootDestin.getLocID())) {
        long freeDiskSpace = this
            .getFreeDiskSpace(SystemParameterFactory.getSystemParameter(SystemParameterConstant.DMS_ARCHIVE_FILE_PATH));
        if (freeDiskSpace != -1 && freeDiskSpace < itemSizeTotal) {
          validation.setSuccess(false);
          validation.setNotEnoughDiskSpace(true);
          validation.setDiskSpaceFree(freeDiskSpace);
          validation.setDiskSpaceRequire(itemSizeTotal);
          return validation;
        }
      }
      // validate setted storage limit
      Double settedLimit = ((DmsRoot) dmsRootDAO.getObjectByID(destinDocument.getRootID())).getStorageLimit(); // M
      if (settedLimit != null && settedLimit.doubleValue() > 0.0) {
        Double usedSpace = dmsRootDAO.getUsedSpaceByRootID(destinDocument.getRootID());
        if ((settedLimit.doubleValue() * 1024 * 1024) < (usedSpace.doubleValue() + itemSizeTotal)) {
          validation.setNotEnoughStorageSpace(true);
          validation.setSuccess(false);
          return validation;
        }
      }
    }

    validation.setLstIdAccept(lstIdHasRight);
    validation.setLstIdReject(lstIdMisRight);
    validation.setLstIdlocked(lstIdBeLocked);
    validation.setLstIdArchived(lstIdBeArchived);
    // joan.xiong update code for bug EIP-1549 by 2007/01/24
    // validation.setSuccess((lstIdMisRight.isEmpty()&&lstIdBeLocked.isEmpty()&&lstIdBeArchived.isEmpty())
    // ? true : false);
    //validation.setMyFavoriteFolder(isMyFavoriteFolderS);
    validation.setLstIdHaveRelationship(lstIdHaveRelationship);//EIP-1663
    validation.setCantCreateShortcut(shortcutFromPersonalToPublic);
    validation
        .setSuccess((lstIdMisRight.isEmpty() && lstIdBeLocked.isEmpty() && lstIdBeArchived.isEmpty() && lstIdHaveRelationship
            .isEmpty()) && !shortcutFromPersonalToPublic? true : false);//EIP-1663
    //joan.xiong update code end
    return validation;
  } 
  
  /**
   * 
   * @param sourceDocument document which to delete.
   * @param isNotDeleteRelationship if not to delete documents which have relationship.
   * @return
   * @throws ApplicationException
   */
  public DmsValidation validateDeleteAction(DmsDocument sourceDocument, boolean isNotDeleteRelationship) throws ApplicationException {
    MtmDocumentRelationshipDAObject mtmRelationshipDAO = new MtmDocumentRelationshipDAObject(sessionContainer, conn);
    DmsDocumentDAObject dmsDocumentDAO = new DmsDocumentDAObject(this.sessionContainer, conn);
    DmsRootDAObject dmsRootDAO = new DmsRootDAObject(this.sessionContainer, conn);
    PermissionManager permissionManager = sessionContainer.getPermissionManager();
    
    DmsRoot dmsRoot = (DmsRoot) dmsRootDAO.getObjectByID(sourceDocument.getRootID());
    String  rootType    = dmsRoot.getRootType();
    Integer rootOwnerId = dmsRoot.getOwnerID();
    
    List tmpLstIdShortcutReject = new ArrayList(); // LEE 06/12/25 EIP-373
    
    List lstIdHasRight = new ArrayList(); //documents(id) have delete right
    List lstIdMisRight = new ArrayList(); //documents(id) have not delete right
    List lstIdBeLocked = new ArrayList(); //documents(id) have been locked by user
    List lstIdBeDeleted = new ArrayList(); //documents(id) have been deleted by other user
    List lstIdBeArchived = new ArrayList(); //documents(id) have been archived
    List lstIdHaveRelationship = new ArrayList(); //documents(id) have relationship except shortcut documents
    //List lstIdIsMyFavorite = new ArrayList();
    Map mpIdToPid = new HashMap(); 
    
    //step 1: validate tree node, add id to lstIdHasRight/lstIdMisRight/lstIdBeLocked
    String[] recordStatus = new String[]{GlobalConstant.RECORD_STATUS_ACTIVE, GlobalConstant.RECORD_STATUS_PENDING};
    List mtmRelation = mtmRelationshipDAO.getListByDocumentID(sourceDocument.getID(), null, GlobalConstant.RECORD_STATUS_ACTIVE);
    List mtmRelatedRelation = mtmRelationshipDAO.getListByRelatedDocumentID(sourceDocument.getID(), DmsDocument.DOCUMENT_LINK, false, GlobalConstant.RECORD_STATUS_ACTIVE, true);
    
    //step 1.1: check root folder
    boolean hasPermissionS = permissionManager.hasAccessRight(this.conn, GlobalConstant.OBJECT_TYPE_DOCUMENT, sourceDocument.getID(),null, null, "D");
    boolean hasBeenLockedS = GlobalConstant.STATUS_LOCKED.equals(sourceDocument.getItemStatus());
    //Add By Jim.Zhou		2006/07/14	Bug	#5697
    /*
    if (DmsDocument.DOCUMENT_LINK.equals(sourceDocument.getDocumentType())){
    	DocumentRetrievalManager docRetrievalManager = new DocumentRetrievalManager(this.sessionContainer, conn);
    	DmsDocument targetDoc = null;
  		targetDoc =docRetrievalManager.getRelationshipTargetDocument(sourceDocument.getID(),sourceDocument.getDocumentType());
      while(DmsDocument.DOCUMENT_LINK.equals(targetDoc.getDocumentType())){
        targetDoc =docRetrievalManager.getRelationshipTargetDocument(targetDoc.getID(),targetDoc.getDocumentType());
      }
      hasBeenLockedS = GlobalConstant.STATUS_LOCKED.equals(targetDoc.getItemStatus());
    }*/
    //Add End
    boolean hasBeenDeletedS = !GlobalConstant.RECORD_STATUS_ACTIVE.equals(sourceDocument.getRecordStatus());
    boolean hasBeenArchivedS = DmsVersion.ARCHIVED_STATUS.equals(sourceDocument.getItemStatus());
    boolean hasRelationshipS = (isNotDeleteRelationship ? (!mtmRelation.isEmpty() || !mtmRelatedRelation.isEmpty()) : false);
    // LEE 06/12/20 EIP-1248 my favorite folder can not be deleted.
    /*
    boolean isMyFavoriteFolderS = false;
    if (DmsRoot.PERSONAL_ROOT.equals(rootType)) {
      RootRetrievalManager rootRetrievalManager = new RootRetrievalManager(sessionContainer, conn);
      DmsDocument rootFolder = rootRetrievalManager.getRootDocument(sourceDocument.getRootID());
      if ("My Favorite".equals(sourceDocument.getDocumentName()) && sourceDocument.getParentID().equals(rootFolder.getID())) {
        isMyFavoriteFolderS = true;
      }
    }
    */
    if (!hasPermissionS || hasBeenLockedS || hasBeenDeletedS || hasBeenArchivedS || hasRelationshipS) {
      if (!hasPermissionS) {
        lstIdMisRight.add(sourceDocument.getID());
      }
      if (hasBeenLockedS) {
        lstIdBeLocked.add(sourceDocument.getID());
      }
      if (hasBeenDeletedS) {
        lstIdBeDeleted.add(sourceDocument.getID());
      }
      if (hasBeenArchivedS) {
        lstIdBeArchived.add(sourceDocument.getID());
      }
      if (hasRelationshipS) {
        lstIdHaveRelationship.add(sourceDocument.getID());
      }
    }else {
      lstIdHasRight.add(sourceDocument.getID());
      mpIdToPid.put(sourceDocument.getID(), sourceDocument.getParentID());

      //step 1.2: validate children node
  
      List documentIds = dmsDocumentDAO.getIDListByParentID(sourceDocument.getID(), null, recordStatus);
      while (documentIds!=null && !documentIds.isEmpty()) {
        //step 1.2.1: add permission to cache
        permissionManager.appendPermissionCache(conn, documentIds, GlobalConstant.OBJECT_TYPE_DOCUMENT, rootType, rootOwnerId);
        
        //step 1.2.2: validate children node in same level
        int batchSize = 100;
        int batchIndex = 0;
        int size = documentIds.size();
        List folderIds = new ArrayList(); //restore folder id for next loop
        while (true) {
          int index_start = (batchIndex++) * batchSize;
          if (index_start>=size) { break; }
          int index_end = (index_start+batchSize)>size ? size : (index_start+batchSize);
          List batchDocumentIds = documentIds.subList(index_start, index_end);
          
          //step 1.2.2.1: get sub document list 100 record everytime
          List documents = dmsDocumentDAO.getListByIDList(batchDocumentIds);
          List hasRelationDocumentID = new ArrayList();
          
          if (isNotDeleteRelationship) {
            List mtmRelationships = mtmRelationshipDAO.getListByDocumentIDList(batchDocumentIds, null, GlobalConstant.RECORD_STATUS_ACTIVE);
            if (mtmRelationships!=null && !mtmRelationships.isEmpty()) {
              for (int i=0; i<mtmRelationships.size(); i++) {
                MtmDocumentRelationship relationship = (MtmDocumentRelationship)mtmRelationships.get(i);
                hasRelationDocumentID.add(relationship.getDocumentID());
              }
            }
            // shortcut will be ignore check.
            List mtmRelatedRelationships = mtmRelationshipDAO.getListByRelatedDocumentIDList(batchDocumentIds, DmsDocument.DOCUMENT_LINK, false, GlobalConstant.RECORD_STATUS_ACTIVE, true);
            if (mtmRelatedRelationships!=null && !mtmRelatedRelationships.isEmpty()) {
              for (int i=0; i<mtmRelatedRelationships.size(); i++) {
                MtmDocumentRelationship relationship = (MtmDocumentRelationship)mtmRelatedRelationships.get(i);
                hasRelationDocumentID.add(relationship.getRelatedDocumentID());
              }
            }
          }
          
          for (int i=0; i<documents.size(); i++) {
            DmsDocument document = (DmsDocument)documents.get(i);
            Integer id = document.getID();
            Integer pid = document.getParentID();
            mpIdToPid.put(id, pid);
            
            //step ....1: add id to lstIdHasRight/lstIdMisRight/hasBeenLocked
            boolean hasPermission = permissionManager.hasAccessRight(this.conn, GlobalConstant.OBJECT_TYPE_DOCUMENT, document.getID(),rootType, rootOwnerId, "D");
            boolean hasBeenLocked = GlobalConstant.STATUS_LOCKED.equals(document.getItemStatus());
            boolean hasBeenArchived = DmsVersion.ARCHIVED_STATUS.equals(document.getItemStatus());
            boolean hasRelationship = hasRelationDocumentID.contains(id);
            if (hasPermission && !hasBeenLocked && !hasBeenArchived && !hasRelationship) {
              lstIdHasRight.add(id);
              if (!DmsDocument.DOCUMENT_TYPE.equals(document.getDocumentType()) && !DmsDocument.DOCUMENT_LINK.equals(document.getDocumentType())) {
                folderIds.add(id);
              }
            }else {
              if (!hasPermission) {
                lstIdMisRight.add(id);
              }
              if (hasBeenLocked) {
                lstIdBeLocked.add(id);
              }
              if (hasBeenArchived) {
                lstIdBeArchived.add(id);
              }
              if (hasRelationship) {
                lstIdHaveRelationship.add(id);
              }
              
              // LEE 06/12/25 EIP-373 add reject shortcut id to tmp list.
              if (DmsDocument.DOCUMENT_LINK.equals(document.getDocumentType())) {
                tmpLstIdShortcutReject.add(document.getID());
              }else {
                Integer mpid = id;
                while ((mpid = (Integer)mpIdToPid.get(mpid))!=null) {
                  lstIdHasRight.remove(mpid);
                }
              }
            }
            
          }
        }
        //step 1.2.3: get next level nodes and loop again
        documentIds = null;
        if (!folderIds.isEmpty()) {
          documentIds = dmsDocumentDAO.getIDListByParentIDList(folderIds, null, recordStatus);
        }
      }
    }
    
    // LEE 06/12/25 EIP-373 
    // If shortcut is in reject list but target document id is in accept list, then this shortcut id must remove from reject list.
    if (!tmpLstIdShortcutReject.isEmpty()) {
      DocumentRetrievalManager retrievalManager = new DocumentRetrievalManager(sessionContainer, conn);
      for (int i=0; i<tmpLstIdShortcutReject.size(); i++) {
        Integer id = (Integer)tmpLstIdShortcutReject.get(i);
        DmsDocument targetDocument = retrievalManager.getRelationshipTargetDocument(id, DmsDocument.DOCUMENT_LINK);
        if (targetDocument!=null && lstIdHasRight.contains(targetDocument.getID())) {
          lstIdMisRight.remove(id);
          lstIdBeLocked.remove(id);
          lstIdBeDeleted.remove(id);
          lstIdBeArchived.remove(id);
          lstIdHaveRelationship.remove(id);
        }else {
          // remove it's parent document id from accept list.
          Integer mpid = id;
          while ((mpid = (Integer)mpIdToPid.get(mpid))!=null) {
            lstIdHasRight.remove(mpid);
          }
        }
      }
      retrievalManager = null;
    }
    
    DmsValidation validation = new DmsValidation();
    validation.setLstIdAccept(lstIdHasRight);
    validation.setLstIdReject(lstIdMisRight);
    validation.setLstIdlocked(lstIdBeLocked);
    validation.setLstIdDeleted(lstIdBeDeleted);
    validation.setLstIdArchived(lstIdBeArchived);
    validation.setLstIdHaveRelationship(lstIdHaveRelationship);
    //validation.setMyFavoriteFolder(isMyFavoriteFolderS);
    validation.setMapIdToPid(mpIdToPid);
    validation.setSuccess((lstIdMisRight.isEmpty()&&lstIdBeLocked.isEmpty()&&lstIdBeDeleted.isEmpty()&&lstIdBeArchived.isEmpty()&&lstIdHaveRelationship.isEmpty()) ? true : false);
    return validation;
  }  
  
  /**
   * get whole tree node id by gived root folder id.
   * lee.lv add
   * @param dmsDocumentDAO
   * @param documentParentIds
   * @param recordStatus
   * @return List
   * @throws ApplicationException
   */
  public List getFullTreeNodeID(
      DmsDocumentDAObject dmsDocumentDAO, 
      List documentParentIds, 
      String[] recordStatus) throws ApplicationException {
    //step 4.1: 
    if (!documentParentIds.isEmpty()) {
      List allDocumentIds = new ArrayList(); 
      
      int batchSize = 100;
      int batchIndex = 0;
      int size = documentParentIds.size();
      while (true) {
        int index_start = (batchIndex++) * batchSize;
        if (index_start>=size) { break; }
        int index_end = (index_start+batchSize)>size ? size : (index_start+batchSize);
        List batchDocumentIds = (index_end<batchSize) ? documentParentIds : documentParentIds.subList(index_start, index_end);
        
        Map pidMap = dmsDocumentDAO.getPIDtoIDMapByIDList(batchDocumentIds, recordStatus, null, null, null);
        for (int i=0; i<batchDocumentIds.size(); i++) {
          Integer id = (Integer)batchDocumentIds.get(i);
          allDocumentIds.add(id);
          List cidList = (List)pidMap.get(id);
          if (cidList!=null && !cidList.isEmpty()) {
            allDocumentIds.addAll(this.getFullTreeNodeID(dmsDocumentDAO, cidList, recordStatus));
          }
        }
      }
      return allDocumentIds;
    }else {
      return new ArrayList();
    }
  }
  
  public String[] validatorFilterDocID(String[] ids)throws ApplicationException{
  	DocumentRetrievalManager docRetrievalManager = new DocumentRetrievalManager(sessionContainer, conn);
  	List tmpDocumentId = new ArrayList();
  	List distinctID = new ArrayList();
  	if(!Utility.isEmpty(ids)){
  		for(int k=0; k<ids.length; k++){
  			distinctID.add(TextUtility.parseIntegerObj(ids[k]));
  		}
  		for(int i=0; i<ids.length; i++){
  			 List parentIdList = new ArrayList();
  			 DmsDocument doc = docRetrievalManager.getDocumentByID(TextUtility.parseIntegerObj(ids[i]));
         if(doc.getDocumentType().equals(DmsDocument.DOCUMENT_LINK)){
        	 MtmDocumentRelationshipDAObject dmsMtmDocumentRelationshipDAO = new MtmDocumentRelationshipDAObject (sessionContainer, conn);
        	 MtmDocumentRelationship docRelationship  = (MtmDocumentRelationship)dmsMtmDocumentRelationshipDAO.getObjectByRelatedDocID(doc.getID(),DmsDocument.DOCUMENT_LINK);
        	 DmsDocument sourceDoc = docRetrievalManager.getDocumentByID(docRelationship.getDocumentID());
        	 if(!Utility.isEmpty(sourceDoc)){
        		 parentIdList = docRetrievalManager.getParentDocumentList(sourceDoc.getID());
             parentIdList.add(docRetrievalManager.getRootFolderByRootID(sourceDoc.getRootID()).getID());
        	 }
  			 }else{
  				 parentIdList = docRetrievalManager.getParentDocumentList(TextUtility.parseIntegerObj(ids[i]));
           parentIdList.add(docRetrievalManager.getRootFolderByRootID(doc.getRootID()).getID());
           parentIdList.remove(TextUtility.parseIntegerObj(ids[i]));
  			 }
         
         boolean found = false;
         if (!Utility.isEmpty(parentIdList)) {
           for (int j=0; j<parentIdList.size(); j++) {
             Integer pid = (Integer)parentIdList.get(j);
             if (distinctID.contains(pid)) {
               found = true;
               break;
             }
           }
         }
         if (!found) {
        	 tmpDocumentId.add(ids[i]);
         }
  		}
  	}
  	String[] newIds = new String[tmpDocumentId.size()];
    for (int i=0; i<tmpDocumentId.size(); i++) {
    	newIds[i] = String.valueOf(tmpDocumentId.get(i));
    }
  	return newIds;
  }
  
  
  /**
   * 
   * @param sourceDocument document which to operate.
   * @param isNotDeleteRelationship if not to delete documents which have relationship.
   * @return
   * @throws ApplicationException
   */
  public DmsValidation validateOperateAction(DmsDocument sourceDocument, boolean isNotDeleteRelationship, String permissionChar) throws ApplicationException {
    MtmDocumentRelationshipDAObject mtmRelationshipDAO = new MtmDocumentRelationshipDAObject(sessionContainer, conn);
    DmsDocumentDAObject dmsDocumentDAO = new DmsDocumentDAObject(this.sessionContainer, conn);
    DmsRootDAObject dmsRootDAO = new DmsRootDAObject(this.sessionContainer, conn);
    PermissionManager permissionManager = sessionContainer.getPermissionManager();
    
    DmsRoot dmsRoot = (DmsRoot) dmsRootDAO.getObjectByID(sourceDocument.getRootID());
    String  rootType    = dmsRoot.getRootType();
    Integer rootOwnerId = dmsRoot.getOwnerID();
    
    List tmpLstIdShortcutReject = new ArrayList();
    
    List lstIdHasRight = new ArrayList(); 
    List lstIdMisRight = new ArrayList(); 
    List lstIdBeLocked = new ArrayList(); 
    List lstIdBeDeleted = new ArrayList(); 
    List lstIdBeArchived = new ArrayList(); 
    List lstIdHaveRelationship = new ArrayList(); 
    //List lstIdIsMyFavorite = new ArrayList();
    Map mpIdToPid = new HashMap(); 
    
    //step 1: validate tree node, add id to lstIdHasRight/lstIdMisRight/lstIdBeLocked
    String[] recordStatus = new String[]{GlobalConstant.RECORD_STATUS_ACTIVE, GlobalConstant.RECORD_STATUS_PENDING};
    List mtmRelation = mtmRelationshipDAO.getListByDocumentID(sourceDocument.getID(), null, GlobalConstant.RECORD_STATUS_ACTIVE);
    List mtmRelatedRelation = mtmRelationshipDAO.getListByRelatedDocumentID(sourceDocument.getID(), DmsDocument.DOCUMENT_LINK, false, GlobalConstant.RECORD_STATUS_ACTIVE, true);
    
    //step 1.1: check root folder
    boolean hasPermissionS = (DmsRoot.PERSONAL_ROOT.equals(rootType)||permissionManager.hasAccessRight(this.conn, GlobalConstant.OBJECT_TYPE_DOCUMENT, sourceDocument.getID(),null, null, permissionChar))? true:false;
    boolean hasBeenLockedS = GlobalConstant.STATUS_LOCKED.equals(sourceDocument.getItemStatus());
    //Add By Jim.Zhou   2006/07/14  Bug #5697
    /*
    if (DmsDocument.DOCUMENT_LINK.equals(sourceDocument.getDocumentType())){
      DocumentRetrievalManager docRetrievalManager = new DocumentRetrievalManager(this.sessionContainer, conn);
      DmsDocument targetDoc = null;
      targetDoc =docRetrievalManager.getRelationshipTargetDocument(sourceDocument.getID(),sourceDocument.getDocumentType());
      while(DmsDocument.DOCUMENT_LINK.equals(targetDoc.getDocumentType())){
        targetDoc =docRetrievalManager.getRelationshipTargetDocument(targetDoc.getID(),targetDoc.getDocumentType());
      }
      hasBeenLockedS = GlobalConstant.STATUS_LOCKED.equals(targetDoc.getItemStatus());
    }*/
    //Add End
    boolean hasBeenDeletedS = !GlobalConstant.RECORD_STATUS_ACTIVE.equals(sourceDocument.getRecordStatus());
    boolean hasBeenArchivedS = DmsVersion.ARCHIVED_STATUS.equals(sourceDocument.getItemStatus());
    boolean hasRelationshipS = (isNotDeleteRelationship ? (!mtmRelation.isEmpty() || !mtmRelatedRelation.isEmpty()) : false);
    // LEE 06/12/20 EIP-1248 my favorite folder can not be deleted.
    /*
    boolean isMyFavoriteFolderS = false;
    if (DmsRoot.PERSONAL_ROOT.equals(rootType)) {
      RootRetrievalManager rootRetrievalManager = new RootRetrievalManager(sessionContainer, conn);
      DmsDocument rootFolder = rootRetrievalManager.getRootDocument(sourceDocument.getRootID());
      if ("My Favorite".equals(sourceDocument.getDocumentName()) && sourceDocument.getParentID().equals(rootFolder.getID())) {
        isMyFavoriteFolderS = true;
      }
    }
    */
    if (!hasPermissionS || hasBeenLockedS || hasBeenDeletedS || hasBeenArchivedS || hasRelationshipS) {
      if (!hasPermissionS) {
        lstIdMisRight.add(sourceDocument.getID());
      }
      if (hasBeenLockedS) {
        lstIdBeLocked.add(sourceDocument.getID());
      }
      if (hasBeenDeletedS) {
        lstIdBeDeleted.add(sourceDocument.getID());
      }
      if (hasBeenArchivedS) {
        lstIdBeArchived.add(sourceDocument.getID());
      }
      if (hasRelationshipS) {
        lstIdHaveRelationship.add(sourceDocument.getID());
      }
    }else {
      lstIdHasRight.add(sourceDocument.getID());
      mpIdToPid.put(sourceDocument.getID(), sourceDocument.getParentID());

      //step 1.2: validate children node
      /*
      List documentIds = dmsDocumentDAO.getIDListByParentID(sourceDocument.getID(), null, recordStatus);
      while (documentIds!=null && !documentIds.isEmpty()) {
        //step 1.2.1: add permission to cache
        permissionManager.appendPermissionCache(conn, documentIds, GlobalConstant.OBJECT_TYPE_DOCUMENT, rootType, rootOwnerId);
        
        //step 1.2.2: validate children node in same level
        int batchSize = 100;
        int batchIndex = 0;
        int size = documentIds.size();
        List folderIds = new ArrayList(); //restore folder id for next loop
        while (true) {
          int index_start = (batchIndex++) * batchSize;
          if (index_start>=size) { break; }
          int index_end = (index_start+batchSize)>size ? size : (index_start+batchSize);
          List batchDocumentIds = documentIds.subList(index_start, index_end);
          
          //step 1.2.2.1: get sub document list 100 record everytime
          List documents = dmsDocumentDAO.getListByIDList(batchDocumentIds);
          List hasRelationDocumentID = new ArrayList();
          
          if (isNotDeleteRelationship) {
            List mtmRelationships = mtmRelationshipDAO.getListByDocumentIDList(batchDocumentIds, null, GlobalConstant.RECORD_STATUS_ACTIVE);
            if (mtmRelationships!=null && !mtmRelationships.isEmpty()) {
              for (int i=0; i<mtmRelationships.size(); i++) {
                MtmDocumentRelationship relationship = (MtmDocumentRelationship)mtmRelationships.get(i);
                hasRelationDocumentID.add(relationship.getDocumentID());
              }
            }
            // shortcut will be ignore check.
            List mtmRelatedRelationships = mtmRelationshipDAO.getListByRelatedDocumentIDList(batchDocumentIds, DmsDocument.DOCUMENT_LINK, false, GlobalConstant.RECORD_STATUS_ACTIVE, true);
            if (mtmRelatedRelationships!=null && !mtmRelatedRelationships.isEmpty()) {
              for (int i=0; i<mtmRelatedRelationships.size(); i++) {
                MtmDocumentRelationship relationship = (MtmDocumentRelationship)mtmRelatedRelationships.get(i);
                hasRelationDocumentID.add(relationship.getRelatedDocumentID());
              }
            }
          }
          
          for (int i=0; i<documents.size(); i++) {
            DmsDocument document = (DmsDocument)documents.get(i);
            Integer id = document.getID();
            Integer pid = document.getParentID();
            mpIdToPid.put(id, pid);
            
            //step ....1: add id to lstIdHasRight/lstIdMisRight/hasBeenLocked
            boolean hasPermission = permissionManager.hasAccessRight(this.conn, GlobalConstant.OBJECT_TYPE_DOCUMENT, document.getID(),rootType, rootOwnerId, permissionChar);
            boolean hasBeenLocked = GlobalConstant.STATUS_LOCKED.equals(document.getItemStatus());
            boolean hasBeenArchived = DmsVersion.ARCHIVED_STATUS.equals(document.getItemStatus());
            boolean hasRelationship = hasRelationDocumentID.contains(id);
            if (hasPermission && !hasBeenLocked && !hasBeenArchived && !hasRelationship) {
              lstIdHasRight.add(id);
              if (!DmsDocument.DOCUMENT_TYPE.equals(document.getDocumentType()) && !DmsDocument.DOCUMENT_LINK.equals(document.getDocumentType()))  folderIds.add(id);
            }else {
              if (!hasPermission) {
                lstIdMisRight.add(id);
              }
              if (hasBeenLocked) {
                lstIdBeLocked.add(id);
              }
              if (hasBeenArchived) {
                lstIdBeArchived.add(id);
              }
              if (hasRelationship) {
                lstIdHaveRelationship.add(id);
              }
              
              // LEE 06/12/25 EIP-373 add reject shortcut id to tmp list.
              if (DmsDocument.DOCUMENT_LINK.equals(document.getDocumentType())) {
                tmpLstIdShortcutReject.add(document.getID());
              }else {
                Integer mpid = id;
                while ((mpid = (Integer)mpIdToPid.get(mpid))!=null) {
                  lstIdHasRight.remove(mpid);
                }
              }
            }
            
          }
        }
        //step 1.2.3: get next level nodes and loop again
        documentIds = null;
        if (!folderIds.isEmpty()) {
          documentIds = dmsDocumentDAO.getIDListByParentIDList(folderIds, null, recordStatus);
        }
      }
    */
    }
    // LEE 06/12/25 EIP-373 
    // If shortcut is in reject list but target document id is in accept list, then this shortcut id must remove from reject list.
    if (!tmpLstIdShortcutReject.isEmpty()) {
      DocumentRetrievalManager retrievalManager = new DocumentRetrievalManager(sessionContainer, conn);
      for (int i=0; i<tmpLstIdShortcutReject.size(); i++) {
        Integer id = (Integer)tmpLstIdShortcutReject.get(i);
        DmsDocument targetDocument = retrievalManager.getRelationshipTargetDocument(id, DmsDocument.DOCUMENT_LINK);
        if (targetDocument!=null && lstIdHasRight.contains(targetDocument.getID())) {
          lstIdMisRight.remove(id);
          lstIdBeLocked.remove(id);
          lstIdBeDeleted.remove(id);
          lstIdBeArchived.remove(id);
          lstIdHaveRelationship.remove(id);
        }else {
          // remove it's parent document id from accept list.
          Integer mpid = id;
          while ((mpid = (Integer)mpIdToPid.get(mpid))!=null) {
            lstIdHasRight.remove(mpid);
          }
        }
      }
      retrievalManager = null;
    }
    
    DmsValidation validation = new DmsValidation();
    validation.setLstIdAccept(lstIdHasRight);
    validation.setLstIdReject(lstIdMisRight);
    validation.setLstIdlocked(lstIdBeLocked);
    validation.setLstIdDeleted(lstIdBeDeleted);
    validation.setLstIdArchived(lstIdBeArchived);
    validation.setLstIdHaveRelationship(lstIdHaveRelationship);
    //validation.setMyFavoriteFolder(isMyFavoriteFolderS);
    validation.setMapIdToPid(mpIdToPid);
    validation.setSuccess((lstIdMisRight.isEmpty()&&lstIdBeLocked.isEmpty()&&lstIdBeDeleted.isEmpty()&&lstIdBeArchived.isEmpty()&&lstIdHaveRelationship.isEmpty()) ? true : false);
    return validation;
  }  
  
  /**
   * Checks there are free space to add the size  under the root.
   * ApplicationException DmsErrorConstant.NO_AVAILABLE_SPACE is thrown when
   * not space is available.
   * 
   * @param size 
   * @param rootID
   * @throws DuplicateNameException
   * @throws ApplicationException 
   * @see com.dcivision.dms.DmsErrorConstant
   * @see com.dcivision.dms.core.RootRetrievalManager
   * @see com.dcivision.dms.core.DocumentOperationManager
   **/
  public boolean checkStorage(String size,String rootID,String folderID) throws DuplicateNameException, ApplicationException {
      boolean isHasstorage = true;
	  RootRetrievalManager rootRetrievalManager = new RootRetrievalManager(sessionContainer, conn);
	  DocumentRetrievalManager docRetrievalManager = new DocumentRetrievalManager(sessionContainer, conn);
      DmsRoot masterRoot = rootRetrievalManager.getRootObject(TextUtility.parseIntegerObj(rootID));
      Double totalSize = masterRoot.getStorageLimit(); //Size in MB
      Double usedSpace = rootRetrievalManager.getUsedSpace(TextUtility.parseIntegerObj(rootID)); //Size in byte
      
      // check the root have enough storage limit space
      if (totalSize.doubleValue()>0.0){
          double newSpaceInMB = ((usedSpace.doubleValue() + TextUtility.parseDouble(size)) / 1024.0 )/ 1024.0;
          log.debug("total space (MB) = " + totalSize);
          log.debug("total usedSpace (byte) = " + usedSpace);
          log.debug("total newSpaceInMB (MB) = " + newSpaceInMB);
          if (newSpaceInMB > totalSize.doubleValue()) {
        	  isHasstorage = false;
          }
      }
      DmsDocument dmsDocument = null;
      // check the folder isExist!
      if(!Utility.isEmpty(folderID)){
    	 dmsDocument = docRetrievalManager.getDocumentByID(TextUtility.parseIntegerObj(folderID));
    	 if(Utility.isEmpty(dmsDocument)){
    		isHasstorage = false;
    	 }else{
    		 if(!GlobalConstant.RECORD_STATUS_ACTIVE.equals(dmsDocument.getRecordStatus())){
    			isHasstorage = false;
    		 }
    	 }
      }
      docRetrievalManager.release();
      rootRetrievalManager.release();
      return isHasstorage;
  }
  
  /**
   * validate before new shortcut action joan.xiong add
   * 
   * @param sourceDocument
   * @param destinDocument
   * @return
   * @throws ApplicationException
   */
  public void validateNewShortcutAction(DmsDocument sourceDocument, DmsDocument destinDocument) throws ApplicationException {
    PermissionManager permissionManager = sessionContainer.getPermissionManager();
    RootRetrievalManager rootManager = new RootRetrievalManager(sessionContainer, conn);
    DocumentRetrievalManager docManager = new DocumentRetrievalManager(sessionContainer, conn);
    MtmDocumentRelationshipDAObject mtmRelationshipDAO = new MtmDocumentRelationshipDAObject(sessionContainer, conn);
    if (DmsDocument.DOCUMENT_LINK.equals(sourceDocument.getDocumentType())) {
      List mtmDocumentIDs = mtmRelationshipDAO.getDocIDByRelatedDocID(sourceDocument.getID(), DmsDocument.DOCUMENT_LINK,
          GlobalConstant.RECORD_STATUS_ACTIVE);
      if(mtmDocumentIDs.isEmpty()) {
        return;
      }
      sourceDocument = docManager.getDocumentByID((Integer) mtmDocumentIDs.get(0));
    }
    /* validate source document */
    // if has shortcut permission
    String sPerm = permissionManager.getPermission(conn, GlobalConstant.OBJECT_TYPE_DOCUMENT, sourceDocument.getID());
    if (DmsDocument.FOLDER_TYPE.equals(sourceDocument.getDocumentType())) {
      if (!((sPerm.indexOf(DmsOperationConstant.DMS_PERMISSION_ALLOW_CREATE_FOLDER) > -1 && sPerm
          .indexOf(DmsOperationConstant.DMS_PERMISSION_ALLOW_RENAME) > -1))) {
        throw new ApplicationException(DmsErrorConstant.DMS_MESSAGE_SOURCE_DOCUMENT_NO_PERMISSION);
      }
    } else {
      if (!((sPerm.indexOf(DmsOperationConstant.DMS_PERMISSION_ALLOW_CREATE_DOCUMENT) > -1 && sPerm
          .indexOf(DmsOperationConstant.DMS_PERMISSION_ALLOW_RENAME) > -1))) {
        throw new ApplicationException(DmsErrorConstant.DMS_MESSAGE_SOURCE_DOCUMENT_NO_PERMISSION);
      }
    }
    // if is archived
    if (DmsVersion.ARCHIVED_STATUS.equals(sourceDocument.getItemStatus())) {
      throw new ApplicationException(DmsErrorConstant.DMS_MESSAGE_UNABLE_SHORTCUT_ARCHIVED_DOCUMENT);
    }
    // if is expired
    if (sourceDocument.isExpired()) {
      throw new ApplicationException(DmsErrorConstant.DMS_MESSAGE_UNABLE_SHORTCUT_EXPIRED_DOCUMENT);
    }

    /* validate destinDocument */
    // if has create document or folder permission
    DmsRoot targetRoot = rootManager.getRootObject(destinDocument.getRootID());    
    if (DmsRoot.PERSONAL_ROOT.equals(targetRoot.getRootType())) {
      sPerm = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    } else {
      sPerm = permissionManager.getPermission(conn, GlobalConstant.OBJECT_TYPE_DOCUMENT, destinDocument.getID());
    }
    if (DmsDocument.FOLDER_TYPE.equals(sourceDocument.getDocumentType())) {
      if (sPerm.indexOf(DmsOperationConstant.DMS_PERMISSION_ALLOW_CREATE_FOLDER) < 0) {
        throw new ApplicationException(DmsErrorConstant.DMS_MESSAGE_TARGET_FOLDER_NO_PERMISSION);
      }
    } else {
      if (sPerm.indexOf(DmsOperationConstant.DMS_PERMISSION_ALLOW_CREATE_DOCUMENT) < 0) {
        throw new ApplicationException(DmsErrorConstant.DMS_MESSAGE_TARGET_FOLDER_NO_PERMISSION);
      }
    }

    // if is compound doc
    if (DmsDocument.COMPOUND_DOC_TYPE.equals(destinDocument.getDocumentType())
        && (DmsDocument.FOLDER_TYPE.equals(sourceDocument.getDocumentType()) || DmsDocument.COMPOUND_DOC_TYPE.equals(sourceDocument
            .getDocumentType()))) {
      throw new ApplicationException(DmsErrorConstant.DMS_MESSAGE_UNABLE_SHORTCUT_FOLDER_COMPOUND_IN_COMPOUNDOC);
    }

    /* validate all of source document and destinct document */
    // if shortcut not public folder to personal folder
    DmsRoot sourceRoot = rootManager.getRootObject(sourceDocument.getRootID());
    if (sourceRoot == null || targetRoot == null) {
      throw new ApplicationException(DmsErrorConstant.ROOT_LOC_NOT_FOUND);
    }
    if (DmsRoot.PERSONAL_ROOT.equals(sourceRoot.getRootType()) && DmsRoot.PUBLIC_ROOT.equals(targetRoot.getRootType())) {
      throw new ApplicationException(DmsErrorConstant.DMS_MESSAGE_UNABLE_SHORTCUT_PERSONAL_PUBLIC);
    }
  }
  
  /**
   * validate parent document exist 
   * @param parentID
   * @return
   * @throws ApplicationException
   */
  public boolean isParentHasDelete(Integer parentID) throws ApplicationException {
    boolean hasRight = true;
    if(Utility.isEmpty(parentID) || "0".equals(parentID.toString())){
      return false;
    }
    DmsDocumentDAObject docDAO = new DmsDocumentDAObject(sessionContainer,conn);
    DmsDocument parentDoc = (DmsDocument)docDAO.getObjectByID(parentID);
    if(GlobalConstant.RECORD_STATUS_INACTIVE.equals(parentDoc.getRecordStatus())){
      return false;
    }
    
    DmsRootDAObject rootDAO = new DmsRootDAObject(sessionContainer, conn);
    DmsRoot root = (DmsRoot)rootDAO.getObjectByID(parentDoc.getRootID());
    if(GlobalConstant.RECORD_STATUS_INACTIVE.equals(root.getRecordStatus())){
      return false;
    }    
    
    DmsDocument tempDoc = (DmsDocument)parentDoc.clone();
    while(!"0".equals(tempDoc.getParentID().toString())){
      tempDoc = (DmsDocument)docDAO.getObjectByID(tempDoc.getParentID());
      if(GlobalConstant.RECORD_STATUS_INACTIVE.equals(tempDoc.getRecordStatus())){
        hasRight = false;
        break;
      }
    }
    
    return hasRight;
  }
  
  /**
   * Judge binding dmsDocument of the workflow  completed. 
   * @param  DmsDocument dmsDocument
   * @return boolean     true:this workflow is completed;
   *                     false:this workflow is not completed;
   * @throws ApplicationException
   */
  public boolean isDmsProgressCompletedByDocument(DmsDocument dmsDocument) throws ApplicationException{
    boolean isProgressCompleted=true;
    MtmWorkflowProgressSystemObjectDAObject mtmWorkflowProgressSystemObjectDAO=new MtmWorkflowProgressSystemObjectDAObject(sessionContainer, conn);
    WorkflowProgressDAObject workflowProgressDAO=new WorkflowProgressDAObject(sessionContainer, conn);
    
    List systemObjectByWorkflowProgressList=mtmWorkflowProgressSystemObjectDAO.getListByBeanClassNameObjectID(DmsDocument.class.getName(),dmsDocument.getID());
    if(!Utility.isEmpty(systemObjectByWorkflowProgressList)){
      MtmWorkflowProgressSystemObject systemObjectByWorkflowProgress=(MtmWorkflowProgressSystemObject)systemObjectByWorkflowProgressList.get(0);
      isProgressCompleted=workflowProgressDAO.checkProgressComplete(systemObjectByWorkflowProgress.getTrackID());          
    }
    return isProgressCompleted;
  }
  /**
   * Judge binding dmsDocument of the workflow  completed in the dmsRoot folder. 
   * @param  DmsRoot dmsRoot.
   * @return boolean true:this workflow is completed.
   *                 false:this workflow is not completed.
   * @throws ApplicationException
   */
  public boolean isDmsProgressCompletedByRoot(DmsRoot dmsRoot) throws ApplicationException {
    DmsDocumentDAObject dmsDocumentDAO = new DmsDocumentDAObject(this.sessionContainer, conn);
    boolean isProgressCompleted = true;
    // Get all document of the current folder
    List documentList = dmsDocumentDAO.getListByRootID(dmsRoot.getID(), "*");
    ListIterator iterator = documentList.listIterator();
    while (iterator.hasNext()) {
      DmsDocument dmsDocument = (DmsDocument) iterator.next();
      isProgressCompleted = isDmsProgressCompletedByDocument(dmsDocument);
      if (!isProgressCompleted) {
        break;
      }
    }
    return isProgressCompleted;
  }
  
  /**
   * check if doc has shortcut in public folder
   * @param docRetrievalManager
   * @param mtmRelationDAO
   * @param dmsRootDAO
   * @param lstIdHaveRelationship
   * @param docId
   * @param failure
   * @return
   * @throws ApplicationException
   */
  private boolean validateShortCut(DocumentRetrievalManager docRetrievalManager, MtmDocumentRelationshipDAObject mtmRelationDAO, DmsRootDAObject dmsRootDAO, List lstIdHaveRelationship, Integer docId, boolean failure,DmsDocument destinDocument) throws ApplicationException {
    List lstRelatedId = mtmRelationDAO.getListByDocumentID(docId, DmsDocument.DOCUMENT_LINK, GlobalConstant.RECORD_STATUS_ACTIVE);
    if (!Utility.isEmpty(lstRelatedId)) {
      DmsDocument tempDoc;
      DmsRoot tempRoot;
      MtmDocumentRelationship tmpMtmDocumentRelationship ;
      for( int j=0;j<lstRelatedId.size();j++) {
        tmpMtmDocumentRelationship = (MtmDocumentRelationship)lstRelatedId.get(j);
        tempDoc = docRetrievalManager.getDocument(tmpMtmDocumentRelationship.getRelatedDocumentID());
        tempRoot = (DmsRoot)dmsRootDAO.getObjectByID(tempDoc.getRootID());
        if(!Utility.isEmpty(tempRoot)&&!tempRoot.getID().equals(destinDocument.getRootID())) {
          lstIdHaveRelationship.add(tempDoc.getID());
          failure = true;
        }
      }
    }
    return failure;
  }
  
  /**
   *  Check this root is not has been binded workflow.
   * @param  Integer rootID.
   * @return boolean true:this root has been binded workflow.
   *                 false:this root not has been binded workflow.
   * @throws ApplicationException
   */
  public boolean isBindWFByRootID(Integer rootID) throws ApplicationException{
    MtmWorkflowRecordObjectLinkDAObject mtmWorkflowRecordObjectLinkDAObject =new MtmWorkflowRecordObjectLinkDAObject(sessionContainer, conn);
    try{
      //get workflow list by rootID.
      List result=mtmWorkflowRecordObjectLinkDAObject.getListByObjectTypeObjectID(GlobalConstant.OBJECT_TYPE_DOCUMENT,rootID);
      return !Utility.isEmpty(result);
    }catch(ApplicationException e){
      log.error(e, e);
      throw e;
    }
  }
}

